Urgent.News

What's breaking now, across thousands of outlets.

Tech

The Pitfalls of Migrating from Xamarin.Forms to .NET MAUI

Why the migration is not a simple upgrade On paper, migrating from Xamarin.Forms to .NET MAUI looks like a namespace change. In practice, the time goes into three areas: custom renderers, navigation, and plugins that are no longer maintained. Microsoft's support for Xamarin ended in May 2024, and the apps still on it are getting harder to publish as Apple and Google raise their minimum SDK…

The transition from Xamarin.Forms to .NET MAUI is not as straightforward as changing a namespace. The migration process involves several challenges, particularly around custom renderers, navigation, and plugins that are no longer maintained. Microsoft ended support for Xamarin in May 2024, which adds pressure as Apple and Google raise minimum SDK requirements for new app submissions.

From a technical standpoint, the upgrade process is relatively simple. The project moves to a single-project format, Xamarin.Forms becomes Microsoft.Maui.Controls, and Xamarin.Essentials is now built directly into the framework. Automated upgrade tools and AI assistants can handle this aspect quite well. However, the difficulties arise when the system cannot understand why a custom renderer was implemented, how the code assumed the order of page loading, or which hidden behaviors an old plugin provided.

One major conceptual shift in MAUI is that it no longer customizes controls through inheritance like Xamarin.Forms did, but through mappers: dictionaries that link each property of a cross-platform control to the native code that applies it. Customizing an Entry control to remove its underline on Android, for instance, would traditionally require writing an entire renderer.

In MAUI, this is achieved by adding an entry to the existing mapper, typically in MauiProgram.cs. This global mapping approach means that any change made to a mapper will apply to every entry in the app, which can lead to unintended consequences if not handled carefully.

Another issue is that the OnElementChanged method, which was used in Xamarin.Forms to mix the creation of the native control, event subscriptions, and cleanup, has no direct equivalent in MAUI. This separation into CreatePlatformView, ConnectHandler, and DisconnectHandler can lead to memory leaks or events firing twice if not implemented correctly. MAUI also has specific requirements regarding the way custom handlers are implemented, with certain methods not being optional.

When it comes to shell and navigation, MAUI recommends using Shell as the recommended approach, replacing older pages like NavigationPage, TabbedPage, and MasterDetailPage. However, this change involves more than just syntax; it impacts how pages are structured and navigated. Shell has specific rules about what types of pages it accepts and how routes and parameters are handled.

For instance, pages not appearing in the Shell visual hierarchy must be registered explicitly, and route parameters need to be managed differently than in Xamarin.Forms.

One of the most time-consuming aspects of the migration is dealing with custom renderers. While MAUI allows old renderers to be registered through AddCompatibilityRenderer, this approach is considered technical debt. It may help in getting the app running quickly but lacks the optimizations of the new pipeline. The recommendation is to use compatibility renderers only as an intermediate step and to clear each remaining renderer through a clear ticket system.

Another pitfall is the change in the type of native control used by handlers. In Xamarin.Forms, handlers often cast to specific native classes. In MAUI, the handler.PlatformView may be a MAUI-derived class rather than the original native class, which can result in an InvalidCastException if not handled properly.

Shell and navigation changes also impact the lifecycle of pages. With ContentTemplate, Shell creates pages the first time they are shown, not when the app starts. This differs from Xamarin.Forms where pages are loaded early in the app's lifecycle. Code that relies on view models being available at app startup may miss certain events in the new system.

Additionally, pages within tabs can have OnAppearing called every time the tab is switched, rather than only once. This requires developers to ensure their data-loading logic is idempotent to avoid redundant operations.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

Why I Created Xeno.JS: Architectural Rigor and Zero Infrastructure Constraints

Introduction How many times have you eagerly started a new TypeScript project, picked the trendy HTTP framework of the month, only to find yourself six months later with a chaotic monolith—hopelessly…

  • Xeno.JS merges .NET/C# architectural patterns with JavaScript's lightweight nature.
  • Implements strict separation of domain logic and infrastructure boundaries.
  • Runtime-agnostic, compatible with various environments without heavy Node.js reliance.

3 Identity Checks When Staging DNS Records Reach the Wrong Production Zone

TL;DR: Treat a DNS change as a typed publication with three identities: environment, customer, and zone. Resolve all three from the request, compare them with independently loaded expectations, and…

  • Verify environment, customer, and zone identities separately
  • Halt write operation if identities do not align
  • Focus on zone reference, not just record value validity

Reading Input in Java

System.in, BufferedReader, and Scanner Every Java beginner eventually asks the same question: "Okay, I can print output — how do I actually read something the user types?" Java gives you a few ways to…

  • System.in.read() reads single byte, returns ASCII value
  • BufferedReader reads entire lines, converts to desired type
  • Scanner simplifies input, handles multiple data types

More from Saturday 3 October →