Recently I’ve been playing around with Platform.Uno – I’ve long held the view that Microsoft’s Xamarin.Forms was a stop-gap in delivering a true cross-platform solution. Whilst Xamarin.Forms is a highly productive platform, it has some severe limitations, specifically around overriding the look and feel of the native controls. This is where Flutter has stolen a lot of mindshare, mainly because customers and companies looking to build apps, no longer look for apps that match the platform style. They’re much more focussed on strong brand presence and great user experience, meaning that a great Material app on iOS will get better reviews and usage than a mediocre app that uses the default iOS style. Platform Uno attempts to deliver on what I believe the Universal Windows Platform (UWP) should have been – taking UWP XAML across iOS, Android and Web Assembly.
Anyhow, here goes with building a TipCalc. I’ll start with the basic UWP application:
- Create a new project based on the Blank App (Universal Windows) template, TipCalc.UWP
- Change the defaults for target and minimum platform versions to ensure the minimum platform version is at least the Fall Creators Update, which results in an application with the following
- Update the XAML to include the basics of a tip calculator (I’ll grab the XAML from https://github.com/nickrandolph/MvxPlus1DaysOfMvvmCross/blob/master/Mvx-01-TipCalc/TipCalc.Uwp/Views/FirstView.xaml)
I’ll create a separate project for our ViewModel:
- Add a new project based on the Class Library (.NET Standard) project template, TipCalc.Core
- Edit TipCalc.Core.csproj to change it to be multi-targetted (ie generates separate dlls for iOS, Android and Windows)
- Note that an alternative way to specify the Sdk attribute is to just put “MSBuild.Sdk.Extras” and then include a global.json file (see example at https://github.com/MvvmCross/MvvmCross/blob/develop/global.json)
- Replace Class1.cs with MainViewModel.cs in a ViewModels folder (I’ll use the FirstViewModel implementation from https://github.com/nickrandolph/MvxPlus1DaysOfMvvmCross/blob/master/Mvx-01-TipCalc/TipCalc.Core/ViewModels/FirstViewModel.cs )
- Change MainViewModel to implement INotifyPropertyChanged
- Add ICalculationService and CalculationService in a Services folder (taken from https://github.com/nickrandolph/MvxPlus1DaysOfMvvmCross/tree/master/Mvx-01-TipCalc/TipCalc.Core/Services)
- Add reference to TipCalc.Core to TipCalc.UWP
- In MainPage.xaml.cs, set DataContext to a new instance of MainViewModel (in the absence of an Mvvm framework such as MvvmCross)
DataContext = new MainViewModel(new CalculationService());
<Project Sdk=”MSBuild.Sdk.Extras/1.5.4″>
<PropertyGroup>
<TargetFrameworks>xamarinios10;MonoAndroid80;uap10.0</TargetFrameworks>
</PropertyGroup>
</Project>
Running the UWP app at this point gives an operational tip calculator
So now, let’s add some Uno goodness. We’ll start by moving the MainPage.xaml into its own class library:
- Add a new project based on the Class Library (.NET Standard) project template, TipCalc.UI
- Remove class1.cs
- Add reference to project TipCalc.Core
- Move MainPage.xaml and MainPage.xaml.cs to the TipCalc.UI project
- Change namespace from TipCalc.UWP to TipCalc.UI for both MainPage.xaml and MainPage.xaml.cs
- Edit TipCalc.UI.csproj to allow it to multi-target (ie generate multiple dlls that target UWP, Droid, iOS etc) and make sure correct build action is set for MainPage.xaml
- Add reference to Uno.UI nuget package (currently in prerelease).
- Final TipCalc.UI.csproj should look similar to
- Add Uno.UI nuget reference to TipCalc.Core. Resulting csproj should look similar to
- Add reference to TipCalc.UI to TipCalc.UWP
<Project Sdk=”MSBuild.Sdk.Extras/1.5.4″>
<PropertyGroup>
<TargetFrameworks>xamarinios10;MonoAndroid80;uap10.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition=” ‘$(TargetFramework)’ == ‘uap10.0’ “>
<GenerateLibraryLayout>true</GenerateLibraryLayout>
</PropertyGroup>
<ItemGroup Condition=” ‘$(TargetFramework)’ == ‘uap10.0’ “>
<PackageReference Include=”Microsoft.NETCore.UniversalWindowsPlatform” Version=”6.1.5″ />
</ItemGroup>
<ItemGroup>
<PackageReference Include=”Uno.UI” Version=”1.31.0-dev.52″ />
</ItemGroup>
<ItemGroup>
<Page Include=”MainPage.xaml”>
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include=”..TipCalc.CoreTipCalc.Core.csproj” />
</ItemGroup>
</Project>
<Project Sdk=”MSBuild.Sdk.Extras/1.5.4″>
<PropertyGroup>
<TargetFrameworks>xamarinios10;MonoAndroid80;uap10.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include=”Uno.UI” Version=”1.31.0-dev.52″ />
</ItemGroup>
</Project>
By all accounts at this point you’ve done a fair bit of lifting with out much to show for it. In fact, because of some absolute genius coding by Microsoft, if you run the UWP application at this point you’ll most likely see a System.AccessViolationException when attempting to navigate to MainPage
This seems to be an issue where you’re attempting to navigate to a page that’s defined in a separate assembly and there are no other XAML pages defined in the head (ie UWP) project. Simple solution is just to add a dummy page to the UWP project that you’ll never use…. but seriously Microsoft??? Oh, and this isn’t the first time this has been pointed out (see https://social.msdn.microsoft.com/Forums/windowsserver/en-US/4efa91ad-fa8f-45f0-9864-c2fd2b24477c/uwpc-accessviolationexception-when-navigating-froma-another-assembly?forum=wpdevelop which links to an old post http://danielvaughan.org/posts/uwp/2015/08/15/UWP-AccessViolationException-when-Navigating-to-a-Page-in-Another-Assembly/)
Right, so the question is – after all this effort, how are we any closer to having a cross platform application. Well, let’s spit out the Android build:
- Add a new project based on the Android App (Xamarin) project template, TipCalc.Droid
- Use the Blank App template
- Uninstall any of the Xamarin.Android.Support nuget packages that are installed by default
- Add reference to Uno.UI nuget package
- Add reference to both TipCalc.UI and TipCalc.Core
- Add a reference to Mono.Android.Export
- Add App.xaml and App.xaml.cs from the TipCalc.UWP project to the TipCalc.Droid project As Link
- Alter MainActivity to the following
- Add an Application class as follows
[Activity(
MainLauncher = true,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize,
WindowSoftInputMode = SoftInput.AdjustNothing | SoftInput.StateHidden
)]
public class MainActivity : Windows.UI.Xaml.ApplicationActivity
{
}
[global::Android.App.ApplicationAttribute(
Label = “@string/app_name”,
LargeHeap = true,
HardwareAccelerated = true,
Theme = “@style/AppTheme”
)]
public class Application : Windows.UI.Xaml.NativeApplication
{
public Application(IntPtr javaReference, JniHandleOwnership transfer)
: base(new App(), javaReference, transfer)
{
}
}
Now we’re good to go. Set the TipCalc.Droid to be the start up project and off we go. The app looks basically the same as it did on UWP
This took quite a bit of effort to get going but now that it’s there, you can imagine that productivity is going to be quite good. Next up I’ll add in the iOS project but I don’t envisage that, nor Web Assembly, being that hard from the samples I’ve seen to date.
Source code available at https://github.com/nickrandolph/UnoSamples