SSW Foursquare

Rules to Better .NET MAUI (was Xamarin) - 11 Rules

Want to build Windows Desktop Applications? Check SSW's MAUI Apps consulting page.

  1. Do you build cross-platform apps?

    cross platform options
    Figure: There are many cross-platform technology options available

    If you're building installable binary apps (as opposed to web apps), it makes sense to use a cross-platform framework so that you don’t need to maintain multiple code bases.

    Writing apps in a single-platform language or framework can lead to fragmentation and technical debt, while writing your app once using a cross-platform technology is the best way to apply DRY to your app as a whole.

    Cross-platform approaches

    There are two approaches to building cross-platform apps. One is to write a single page application (SPA) and use a wrapper technology like Cordova or Electron to turn your SPA into an installable app. This is a good approach if you already have a fully functioning web app and want it to be available for users to install via an app store.

    The other approach is to use an API that is a single, cross-platform abstraction that compiles to native code executable on your target platforms. This is the approach taken by .NET MAUI (previously Xamarin), Flutter, and React Native.

    When to use a web wrapper

    Use a web wrapper, like Cordova or Electron, if your SPA is already built and offers all (or almost all) of the functionality that you want your app to provide.

    When to use an abstracted cross-platform API

    Web wrapper technologies are good for apps that also work as web apps (and potentially already exist as web apps), but they also suffer from some of the limitations of web apps.

    If you need multi-threading, or access to hardware features, platform or operating system APIs or system processes, then using an abstracted cross-platform API is a better option.

    Which cross-platform framework to choose

    If you need access to specific platform APIs, you may not find them available in the web wrapper options (and almost certainly not via 1st party support). In this case, you could rule out using a web wrapper.

    If you already have a .NET development team and your solution is built on .NET, it makes sense to use .NET MAUI. .NET MAUI lets you write your UI in XAML or Blazor, so if you already have a Blazor web solution, you can share your UI controls in a Razor Class Library. You could also share other things like DTOs and authentication logic between Blazor, ASP.NET Core, and .NET MAUI apps.

    If you already have a strong React team, it may make sense to use React Native. There is a bit of a learning curve to go from React to React Native, but it still builds upon your team’s existing skills.

    Choose Flutter if you like Dart.

  2. Cross-platform Apps - Do you choose the right mobile framework?

    If you need to build a binary app (rather than a web app) that will run on multiple platforms (e.g. iOS, Android, macOS, Windows), you can either build and maintain multiple versions of the app - one for each platform - or you can use a cross-platform (cross compiler) framework to build one app that runs on all of them.

    Video: Mobile frameworks - Comparing the big guns: Ionic, Electron, React Native, Flutter and .NET MAUI (6 min)

    tutorial develop apps ios 2x
    Bad example - An app built in Swift can run on iOS and macOS (and Apple TV if you need it), but can't run on any other platforms

    winui3 addnewitem
    Bad example - An app built with WinUI can run on Windows, but can't run on any other platforms

    single project
    Good example - An app built with .NET MAUI can run on Windows, macOS, iOS, Android, and several others

    With many cross-platform frameworks available to suit every team and product, there is very little (or no) reason to build single-platform apps anymore. Even if you only initially intend to target a single platform, by using a cross-platform framework, you give yourself the opportunity of targeting additional platforms in the future.

    Understand the different approaches to cross-platform apps

    Cross-platform app frameworks generally come in 3 flavors: PWAs, web wrappers and native executables.

    PWAs are the fastest way to transform your website into a cross-platform app. However, you miss out on the native feel of having it in the app store.

    Web wrappers take a single page application written in (or transpiled to) JavaScript, and wrap them in a web view. The web view is just like a browser tab running the SPA on the device, but without the browser navigation buttons (so you can't go to a different address for example). Examples using this approach are Ionic or Electron.

    Web wrappers are an attractive option for teams with an existing JavaScript product. They can be quick to get up and running and leverage your existing skills. But they have significant limitations compared to native executable frameworks, particularly when it comes to accessing platform APIs and features, e.g. encryption, Bluetooth, AR APIs like ARKit (iOS) or ARCore (Android), etc.

    Web wrappers are good for standing up quick prototypes or PoCs, but are not recommended for long term supported solutions.

    Native executables (aka cross compiler) use a cross-platform API to build the app, but compile native binary executables for each target platform. Examples using this approach are .NET MAUI, Flutter or React Native.

    How to choose the right framework

    Developers building apps that target multiple platforms are in the ideal position. Several frameworks exist to fill this niche, meaning developers have the luxury of choosing the best fit for their needs. When choosing a cross-platform framework for your team, ask the following questions:

    • What skills do we already have?

      • For .NET teams, .NET MAUI is the best choice. It targets all the major platforms, it's performant, highly customisable, and leverages your existing skills. It also integrates well with your existing solution

    Video: Clean Architecture in .NET MAUI and ASP.NET Core with Matt Goldman (1 h 23 m)

    • For a React team, React Native may be a smoother transition. There's still a learning curve from React to React Native, but that curve may not be as steep for teams with existing React skills as for other teams.
    • For teams with good Angular knowledge, Ionic is worth considering (but as per above, not recommended for more than a quick prototype).
    • What platforms do we need to target?

      • Not all cross-platform framework targets every platform. Most will work on the 'core 4' (macOS, iOS, Android and Windows). .NET MAUI will also work on watchOS, wearOS and Tizen. If you need to target Linux and/or the web, you should consider Uno platform.
    • What level of 1st party support do we need?

      • Some of these frameworks are maintained by big tech companies. .NET MAUI for example is maintained by Microsoft, Flutter is maintained by Google, and React Native is maintained by Meta. However, they are not equal in terms of first party support. .NET MAUI, for example, has an Essentials API that provides access to many cross-platform hardware features, whereas many of these need to be loaded via 3rd party plugins in other frameworks.

    Developers are spoiled for choice; we have the luxury of choosing from many cross-platform frameworks. Most of them are very mature and stable, allowing us to build first-class apps.

    Frameworks - Pros and Cons

    Here is a nice graphic that gives a quick run-down of the pros and cons of different frameworks:

    cross platform apps pros and cons
    Figure: Mobile frameworks all have different strong suits

    Summary

    ❌ Don't use a web wrapper. They might seem tempting as a quick option to start with, but you will cause yourself pain πŸ‘ŽπŸ» down the line.

    1200px Ionic logo landscape svg
    Bad example - Web wrappers cause you pain in the long run

    βœ… Choose a native executable framework. They let you build the best apps in the long run.

    xplat native
    Good example - Choosing a native executable cross-platform framework lets you build the best apps with minimal pain πŸ‘πŸ»

    βœ… Do choose .NET MAUI if your team and/or solution already use .NET.

    maui sln
    Great example - .NET MAUI is the best choice for .NET developers

  3. Do you know how to build for the right platforms?

    Picking the right development environment is important, and which platforms you want to target will influence that decision.

    PlatformBuild on Mac?Build on Windows?
    Androidβœ…βœ…
    iOS/iPadOSβœ…βŒ
    UWP (Windows)βŒβœ…
    macOSβœ…βŒ
    tvOSβœ…βŒ
    watchOSβœ…βŒ
    wearOS (many non-Apple and non-Samsung wearables)βœ…βœ…
    Tizenβœ…βœ…

    Figure: The platforms you can target with each development environment – in most situations a Mac works best

    If you want to develop for Android, wearOS, or Tizen, you can use Visual Studio on either Windows or macOS. If you want to target UWP, you must use Windows. If you want to develop for iOS, tvOS, macOS, or watchOS, you can now develop using Windows or Mac (using Hot Restart on Windows) but must use a Mac to publish your app to the App Store. If you want to target all these platforms you will need access to both Windows and Mac.

    Tip: If you use an Intel based Mac you can run Windows through virtualization, using VMware Fusion, Parallels or VirtualBox. If you use Windows, there are cloud-based Mac services you can use for your Apple OS builds.

  4. Do you use the MVVM Pattern?

    .NET MAUI and Xamarin has been adhering to the MVVM design pattern since their inception. While .NET MAUI provides developers with additional flexibility by adopting the MVU pattern (see: Introducing .NET Multi-platform App UI), MVVM remains a widely popular approach for architecting mobile applications.

    MVVM allows for loose coupling between data, business logic, and UI. In .NET MAUI, UI is usually defined in XAML (although you can declaratively define your UI in C# code too). Your UI is called a 'view' - a view can be a page or a UI element, although UI elements that are not complete pages are more often referred to as controls.

    mvvm bad
    Figure: Bad Example - Logic and properties are in the code behind, which decreases maintainability and testability

    mvvm good
    Figure: Good Example - Values are bound to properties of the ViewModel, and actions are bound to Commands in the ViewModel

    .NET MAUI supports MVVM out of the box, but there are several MVVM frameworks available that enhance this functionality. For example, some MVVM frameworks support "convention over configuration", allowing you to just code your View and ViewModel and let the framework hook them up for you. Some include:

    • MVVM Toolkit: MVVM Toolkit is maintained and published by Microsoft. The framework significantly reduces the amount of boilerplate code by applying Roslyn source generators.
    • Prism: Prism is an MVVM framework that was developed initially for WPF but has since been ported to Xamarin Forms and then to .NET MAUI. It is stable and mature. For a long time Prism sustained itself through donations, with the founders contributing significant time and effort for free. However, they eventually shifted to a paid model (with a free option) to sustain and further develop the library.
    • FreshMVVM: FreshMVVM is a framework that was built from the ground up specifically for Xamarin Forms and also migrated to .NET MAUI. It is open-source and maintained by a Microsoft MVP.
    • MVVMLight: This framework, built especially for Xamarin, was archived in 2021 in favour of its successor, the MVVM Toolkit.
  5. Do you use design time data?

    UPDATE: XAML Previewer was deprecated in Visual Studio 2019. For .NET MAUI use Hot Reload

    The XAML previewer in Visual Studio is a useful tool for designing your Xamarin UI. One limitation is that often your controls are bound to properties in your ViewModel (see rule: Do you use the MVVM pattern?), meaning that you can't see what your UI will look like with the data loaded, without building and running your app.

    design time bad
    Figure: Bad example - Screenshot of XAML previewer with blank controls

    A simple solution to this problem is to use design-time data. By importing the relevant namespaces into your XAML file, you can specify placeholder data that the previewer interprets to show how your UI will render with data loaded.

    These are the namespaces to import, and the declaration to use them:

    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"

    design time good
    Figure: Good example - Screenshot of XAML previewer with design-time data

    If your Xamarin and Visual Studio versions are up to date these namespaces will automatically be included in any new XAML file.

  6. Do you know where to find the best .NET MAUI resources?

    The following is a list of resources for learning, staying up to date with, and using .NET MAUI.

    General

    Awesome .NET MAUI (evolution of Awesome Xamarin Forms) is a curated list on GitHub of a bunch of resources for .NET MAUI. This includes controls, effects, behaviors, tools, and more. It's a good 'one-stop-shop' for everything related to .NET MAUI.

    Sands of MAUI are newsletter-style blogposts regularly published on Telerik's website. It's a great source to keep yourself up to date as they cover the latest news about .NET MAUI.

    Learning

    Gerald Versluis regularly posts great practical videos on .NET MAUI.

    If you are new to the technology and want to know the basics, check out this 4-hour crash course by James Montemagno.

    The best book for learning .NET MAUI is .NET MAUI in Action by Matt Goldman.

    Events

    SSW hosts a MAUI Hack Day every year.

    Controls

    .NET MAUI has a thriving and passionate community and ecosystem. Many .NET MAUI developers have made awesome controls available either as open-source libraries, NuGet packages, or both. A lot of these can be found at the Awesome .NET MAUI GitHub page linked above. Additionally, a number of commercial options are available.

    .NET MAUI Community Toolkit (evolution of Xamarin Community Toolkit): This is a community-driven set of controls, effects, behaviors, and extensions that make add a lot of functionality you are likely to reuse in your projects.

    SyncFusion: SyncFusion provides a set of beautiful controls and templates for a range of use cases and scenarios. There is also a community license, which allows you to use it free for anything earning less than $1m a year.

    Telerik: Telerik provides a range of controls focused on enterprise and business applications.

    DevExpress: DevExpress provides a library of high-performant, enterprise-grad controls for mobile .NET MAUI applications. There is a free option for registered users.

  7. Do you optimise your Android builds and start-up times?

    .NET MAUI provides several ways to optimize an Android application. Some of them complement each other while others can be mutually exclusive.It's important to understand what options you have at hands and how they affect your application.

    When it comes to app optimizations, developers usually try to strike the right balance between:

    • App build time
    • App size
    • App performance (execution speed and memory)

    Quite often improvements in one area lead to degradation in another. In most cases it's a tradeoff which developers need to take depending on their circumstances. Different Debug and Release configurations partially address this problem, but may lead to configuration-specific bugs.

    .NET MAUI Android combines two worlds - native Android and .NET, which means that optimization can be performed in both worlds.

    Native Layer optimizations

    These optimizations are not specific to .NET MAUI and are used in other cross-platform frameworks or native Android development.

    Use AAB (Android App Bundle or just App Bundle)

    ❌ Debugβœ… Release

    .aab files are used only for publishing on Google Play and cannot be installed on Android devices. AAB allows smaller builds to be targeted to individual hardware specifications by packaging only required resources (e.g. icons, images). It also deligates app signing to Google Play.App Bundle is mandatory for Google Play since 2021.

    Use R8 (instead of ProGuard)

    ❌ Debugβœ… Release

    R8 is a code shrinker and obfuscator which processes only native java/kotlin code. It doesn't touch your .NET code. As R8 increases build time it's only recommented for Release configuration.

    Use AAPT2 (instead of AAPT)

    βœ… Debugβœ… Release

    AAPT2 parses, indexes, and compiles the resources into a binary format that is optimized for the Android platform.

    .NET Layer optimizations

    These optimizations are .NET MAUI specific.

    Concurrent GC

    βœ… Debugβœ… Release

    Concurrent GC improves app performance by collecting garbage alongside the running program. This approach prevents program from freeze which happen for non-concurrent GCs.

    AOT

    ❌ Debugβœ… Release

    Whereas iOS enforces AOT (ahead of time) compilation, Android supports (and uses by default) JIT (just in time) compilation which happens at runtime, but AOT can be enabled on Android to improve performance. As the code will be pre-compiled, it comes at a cost of significantly larger app size and longer build times.

    There are several types of AOT:

    • Full AOT (comes from Mono)
    • Profiled AOT (comes from Mono, a.k.a Startup Tracing)
    • Native AOT (comes from .NET; not available for Android, but .NET MAUI team is experimenting with iOS)

    Profiled AOT provides finer control over the trade-offs between Android APK size and startup time as compared to the Full AOT compilation option.Instead of compiling as much of the app as possible to unmanaged code, Profiled AOT compiles only a particular set of managed methods that represent the most expensive parts of application startup in a blank app. This approach consumes less space in the APK compared to the Full AOT compilation option while still providing similar app startup performance improvements.

    AOT increases app size and build time (especially Full AOT). Recommended for Release.

    LLVM

    ❌ Debugβœ… Release

    Mono support two compilation engines, a fast, JIT-friendly compilation engine which does not generate very fast code, and a slower compilation engine based on the LLVM optimizing compiler that produces superior code.When set LLVM will be used when Ahead-of-Time compiling assemblies into native code.

    If Full AOT is not enabled, this property is ignored.

    Increases build time. Recommended for Release.

    IL Stripping

    ❌ Debugβœ… Release

    $(AndroidStripILAfterAOT) removes IL code after performing AOT as it is no longer required.

    More information

  8. Do you use Hot Reload?

    Developing mobile apps presents unique challenges compared to web or desktop development. One of the problems is that when using MVVM or using dynamic data on a page, you need to run your app to populate the data and see what your UI will actually look like.

    hot reload bad
    Figure: Bad Example - rebuilding your app every time to see small UI changes

    To get around this problem use Hot Reload. This lets you make changes to your XAML while debugging your app - as soon as you save your UI will update, without having to stop and rebuild your app.

    hot reload good
    Figure: Good example - hot reload enable screenshot Windows

    Tip: This works on the iOS simulator, the Android emulator, and physical iOS and Android devices.

  9. Do you use know how to consistently style your app?

    Branding is important in any product, and especially a mobile app. Xamarin offers several ways to define and ensure consistent styling throughout your app:

    • Resource Dictionaries
    • CSS
    • Visual

    xamarin style bad
    Figure: Bad Example - same styling defined and repeated multiple times

    xamarin style good
    Figure: Good Example - Styles defined once in resource dictionary and applied to controls

    Resource Dictionaries and CSS provide similar capabilities, but Resource Dictionaries are the best way to style your Xamarin applications. CSS in Xamarin is not full, web-standards compliant CSS, but rather an alternative way to write Xamarin styles than Resource Dictionaries. CSS in Xamarin does not currently support the full range of properties available, but it may be more comfortable for those familiar with web development.

    Visual offers a much more granular level control over the look and feel of your application. However, this increase in granularity comes with a proportionally increased level of complexity. Rather than providing a style, you need to define a custom renderer for every control you want to define the look of.

    Visual is suitable for large teams with a design heavy focus, where branding is of paramount importance. The advantage of Visual is that you can specify it at individual control level, at page level, or at whole of app-level to ensure your entire app is consistent.

  10. Do you conduct cross-platform UI Tests?

    Any changes you make to your app risks breaking existing functionality. Having a suite of automated tests that you can run prior to any release reduces the risk of releasing a product with new features that don't work, or that breaks existing features. It also means that you can run these tests as part of your CI/CD pipeline.

    Every control in .NET MAUI exposes the AutomationId property, which allows a UI testing framework to find and interact with contols. This article demonstrates how you can write and run your UI tests with Appium.

    Unlike Xamarin, .NET MAUI doesn't come with a built-in UI testing framework - Xamarin.UITest. Technically, you still can use Xamarin.UITest with .NET MAUI, but only to unblock your team to migrate from Xamarin. For details, see this video by Gerald Versluis.

  11. Do you test .NET MAUI apps on different devices?

    .NET MAUI (Multi-platform App UI) is a framework that enables developers to create cross-platform applications for different devices efficiently. However, the efficiency of this framework could mean nothing if the developed apps are not tested on various devices, especially the older once.

    Testing mobile apps on different devices helps in ensuring compatibility across multiple platforms and versions. Different devices have unique hardware, operating systems, and screen resolutions. Therefore, testing an app on different devices ensures that the app can work efficiently on all platforms. For example, an app that runs smoothly on an Android device might be incompatible with an iPhone's operating system, resulting in a suboptimal user experience and potential bugs.

    Option 1: Use emulators

    There are several ways that developers can test mobile apps on different devices to ensure that they are running smoothly and providing the best user experience possible. One approach is to use emulators, which are programs that simulate the behaviour of specific devices and operating systems. These emulators can be very helpful for testing basic functionality, but they may not accurately represent all aspects of the user experience, such as touch sensitivity and screen size.

    Some of the popular .NET MAUI emulator apps are:

    • iOS Simulator: it allows developers to preview their apps on iOS and macOS devices.
    • Android Emulator: it allows developers to test their apps on various Android devices.
    • Tizen Simulator: it allows developers to test their apps on Tizen devices.

    Option 2: Use a cloud-based service

    Testing mobile apps with cloud-based services like BrowserStack offers several advantages to developers. Firstly, it allows them to easily access a wide range of real devices and browsers for testing, enabling comprehensive and efficient app evaluation. The developers can run tests on multiple operating systems, screen sizes, and resolutions, ensuring that their MAUI app performs seamlessly across various platforms. Additionally, these cloud-based services provide instant access to devices, eliminating the need for physical hardware and saving both time and resources. However, reliance on such cloud-based services introduces substantial latency to your inner development loop, and might not be the best option for development.

    Apart from BrowserStack, there are several services that offer similar functionality:

    • Sauce Labs
    • LambdaTest
    • Experitest
    • CrossBrowserTesting.

    Another effective way to test mobile apps on different devices is to use real hardware. This allows developers to see how the app performs on a variety of devices with different screen sizes, processing power, and other specifications. One approach is to borrow or purchase multiple devices to test on, but this can be time-consuming and expensive. The better way is to have a device policy for your mobile dev teams.

    Regardless of the approach used, it is important to test on a variety of devices to ensure that the app works well for all users. This can include testing on both iOS and Android devices, as well as different versions of each device and operating system. Additionally, testing on different screen resolutions and aspect ratios can help identify any design issues that may arise. By thoroughly testing on a range of devices, developers can ensure that their MAUI app is high-quality, user-friendly, and reliable across all platforms.

We open source. Powered by GitHub