What’s up with .NET MAUI?
If you have seen any news about .NET 6.0 or anything Xamarin.Forms related, you have probably heard about the new framework called .NET MAUI. Microsoft has decided that the next major iteration of Xamarin.Forms will instead be replaced by the .NET Multi-platform App UI: MAUI. Since Xamarin support is intended to end in November 2023, most developers relying on Xamarin.Forms will need to prepare for MAUI migration in order to receive the latest features and mobile operating system support. If you are one of those developers, there are a few things to consider before tearing apart your codebase to prepare for these changes.
First of all, MAUI has a few things different than Xamarin.Forms. On a high level, you should consider it as a separate framework even though it is seemingly just a fork of Xamarin.Forms.
MAUI Benefits over Xamarin
The new, big push that MAUI is trying to make is keeping code contained all within one project. Before, you had one shared project and an individual project for each platform you targeted, the latter referencing the former project. With this new change, you get a couple of additional benefits:
Shared Asset Libraries
Unlike previous Xamarin projects, assets such as fonts or images can be kept in the resources of a single project. Before this, copies of every asset were found in each project. This can drastically minimize the size of your project.
Additional App Model Support
A bit tangent from the Model-View-ViewModel pattern, MAUI has also chosen to additionally support two new patterns: Model-View-Update pattern, and the Blazor client-side web UI. MVU prioritizes rapid prototyping of visuals utilizing the hot-reload feature Xamarin has previously boasted, and Blazor also allows rapid prototyping with HTML and CSS. These two additions are optional, but useful for those who want to take the time to learn other fast development patterns.
Multi-Targeting to support more platforms
This single-project structure does keep run configurations minimal, hiding a lot of the details that were necessary with previous Xamarin platform-specific projects. This quality of life change is intended to simplify the new MacOS and Windows development environment.
To see the full rundown of why Microsoft decided to create MAUI, you can read the introductory blog post here.
MAUI’s Early-Access Limitations
As with any new release of a vast framework, MAUI has had its fair share of complications. As of now, a lot of functionality that may be required within your existing Xamarin.Forms application may be problematic when moved into the new .NET 6.0 ecosystem. For example, translating native library bindings to .NET 6.0 may cause some difficulty. Personally, I have had a large struggle trying to get library bindings to work using the new Android and iOS .NET library projects. Having to accept that some of these niche issues may not be prioritized soon is a risk of using early preview open source software; something you will have to consider.
MAUI also experienced some delays to not be bundled with the .NET 6.0 release, and is now planned to deploy by end of Q2 2022. You should expect it to postpone any additional production migration work. Since they heavily recommend not using MAUI for production apps, this adds a difficult timeframe for developers to keep up with. For each new release of MAUI, a new batch of migration steps are necessary to upgrade your project to the latest one. I’ll do what I can to update this blog post as the releases come in.
Is Migration To MAUI Right for My Project?
The first consideration when facing a mandatory refactor is to think about framework alternatives. If you are going to spend the time refactoring, it may be the time to invest into a different framework if your project can benefit from it. These are all questions you should be asking yourself when starting a mobile project, and don’t hurt to revisit.
Does my project clash with the single-project strategy?
The single-project strategy is commonly mentioned as one of MAUI’s great benefits, it can limit logic into a single project as opposed multi-project relationships and dependencies. If your existing project has multiple inner projects and relationships, it may not fit well with the fundamental concept that Microsoft is pushing. This is an important question to ask yourself and your team, considering the preferred project structure and optimal organization.
Does my project have a heavy reliance on platform-specific logic or external libraries?
Due to the varied platform-specific bugs MAUI has in its early iterations, it may be difficult to implement cross-platform logic tightly coupled to other libraries. And in the short term, a lot of existing libraries will have to be rewritten to work with MAUI as opposed to Xamarin.Forms. Only a few Xamarin.Forms libraries have had the chance to port their libraries to MAUI, especially this early into the release cycle. Additional refactor work may be necessary to duplicate existing functionality into MAUI.
Is my product only meant for mobile, do we not care about Windows/MacOS releases?
This question is more important for your stakeholders, as it pertains to the public release and usage of the app. If you have nothing to gain with Windows/MacOS releases, you may want to reconsider this migration. This major MAUI feature has been advertised as a significant reasoning for the framework, and may not be worth the effort.
What is the scope of my app? Is my project expected to grow significantly over time?
If your project will endure a variety of iterations of the years, you may want to consider using a different framework. From what we can tell about MAUI and most other cross-platform libraries, they optimize making proof-of-concept or rapid-development applications. Cross-platform frameworks often make assumptions about inner frameworks, layering over the existing Android and iOS platforms. This leaves out a lot of functionality, something noticeable on a large and evolving project.
MAUI’s future timeline is also in the dark, as most timelines point towards the release of MAUI – not the long-term lifespan or support warranty. Since so much is in the unknown, especially with the far-out .NET 7.0 release, a lot adds up to consider MAUI as an unacceptable framework for a large project of the sort. Thankfully if you are still porting an existing project, a lot of methods exist to import .NET business logic/functionality to native frameworks. See: Xamarin .NET Embedding and MAUI Embedding Extensions. Note: Embedding Extensions has very little documentation on the subject.
If your scope is expected to grow, a native project may suit your interests best. Other cross-platform frameworks attempt to solve this problem, but the effort to maintain two separate apps is worth the control it provides.
If your project answers yes to several of these questions, the best option is to use a different mobile framework. One should especially consider using the native frameworks for both iOS and Android, since those have the most opportunity for control over a robust app. To consider alternatives cross-platform frameworks, check out this additional SEP blog post.
Migration To-Do’s
If you choose to continue down the path of MAUI migration, here’s a rundown of the steps you will need to take. This guide is mostly based on the official migration guide, as well as my own experiences porting projects and exploring the current state of MAUI.
You could also use the .NET Upgrade Assistant to convert your project automatically, but tread with caution. Also, this tool is not supported on macOS due to dependencies to Visual Studio.
The New MAUI Project
Windows Install
You can use the latest preview version of Visual Studio 2022 to install MAUI and create your new project. You can see the official guide here.
MacOS Install
You will need to update via command line to install the MAUI workload. You can find the guide here.
Once your new project is created, move it into the Solution that your existing project lives in. Drag it into the same folder with the rest of your projects, and then add an existing project through the solution explorer. This was the simplest way to make the next migration steps easier.
Once in place, you can move all your shared logic to the new project. The following namespaces will have to be changed:
xmlns="https://xamarin.com/schemas/2014/forms" xmlns="https://schemas.microsoft.com/dotnet/2021/maui" using Xamarin.Forms using Microsoft.Maui AND using Microsoft.Maui.Controls using Xamarin.Forms.Xaml using Microsoft.Maui.Controls.Xaml
Other namespaces/names may have changed, like for example Xamarin.Forms.Color changing to Microsoft.Maui.Graphics.Colors. Proceed with caution.
Moving Over Platform Logic
Most cross-platform functionality still exists in the MAUI ecosystem, just under a different name.
Custom Renderers
Custom renderers are no longer indicated by attributes – they are instead registered within the MauiProgram.cs file, inside the builder. For example, a new renderer named “CustomClass” can add a new section that looks like:
.ConfigureMauiHandlers(handlers => { #if ANDROID handlers.AddHandler(typeof(CustomClass), typeof(ProjectName.Droid.CustomRenderers.CustomClassRenderer)); #elif IOS handlers.AddHandler(typeof(CustomClass), typeof(ProjectName.iOS.CustomRenderers.CustomClassRenderer)); #endif }
The “CustomClassRenderer” class will extend a base renderer under the same name as their Xamarin variant, however it will be under a different namespace. For example, Xamarin.Forms.Platform.Android.ViewRenderer changes into Microsoft.Maui.Controls.Compatibility.Platform.Android.ViewRenderer.
Dependency Interfaces / Cross Platform API
Default: Partial Classes
Officially, MAUI recommends using partial classes for different implementations of dependency services. The base class can be defined outside of the Platforms folder, and contains a definition of the partial methods implemented by each platform.
For more examples using partial classes, check out the documentation.
Better: Dependency Injection
The best recommendation to keep your interfaces intact would be to use some dependency injection framework, for example Autofac. You can install individual modules based on the compiler flags, and keep the same structure as the previous Xamarin.Forms dependency interfaces. This is also a best practice for any large framework with multiple services. Bonus points if you already use Autofac to control platform-specific modules and dependencies, making your migrations even easier.
Native Library Bindings
Part of the .NET 6.0 migrations included the transition of Xamarin.Android and Xamarin.iOS to “.NET for Android” and “.NET for iOS”. This includes the requirement of changing your existing Android and iOS binding library projects to a different project type.
With the dotnet CLI fully upgraded, you should be able to see the list of possible project types to create. To get this information, enter:
dotnet new --list
You should be presented with a list of templates available to create. Ensure that the following templates exist:
Template Name Short Name Language Tags
Android Java Library Binding (Prev... android-bindinglib [C#] Android/Mobile iOS Binding Library (Preview) iosbinding [C#] iOS/Mobile
Using the short name, use dotnet new to initialize a project of each type. Once initialized, you can copy over your jars and iOS libraries and their corresponding additions/api definitions/structs. The project structure should likely be the same, and they should be accessible for your MAUI project to use in your platform-specific code.
NOTE: Being one of the smaller targets on Microsoft’s radar, these features have yet to be fully tested. You may run into several issues getting your local bindings to work with MAUI. Keep note of the publicly tracked issues around MAUI & .NET for iOS/Android, and proceed with caution.
UI Testing
If you are already using an app-agnostic test framework, you have nothing to change! Testing frameworks like Appium should work out-of-the-box with MAUI. Your migration will only be affected by the way your tests communicate with the app. If it isn’t through a direct xamarin call, you should be fine.
Final Thoughts
This coming release will certainly be a frustrating change for Xamarin.Forms developers. The only options here are either force your app through a migration, or forego all future support for Xamarin.Forms. This includes any subsequent platform releases for Android or iOS, as well as any bugfixes or external 3rd party library support.
Overall, your first step is to discuss the future with your team. The mandatory migration can be seen as an opportunity – one to finally put in the work for a framework which benefits your project the most. One question I want everyone to consider is:
If you had to start the project from scratch, how would you do it?
While this is in no way feasible for most large-scale projects, the thought experiment brings up important ideas to consider. As the inevitable software rot accumulates, taking a fresh step into a new framework can be beneficial for both the end-users of your mobile application and your developers.
Ask around, see if MAUI is right for you. Spend a sprint or two spiking a couple of mobile frameworks, importing your code. It will be worth the investment.