I've been meaning to write about this topic for a while.
MAUI looks great on paper. In fact, it has looked good on paper for years. Microsoft went a long way through UI and application frameworks: VBA, WinForms, MFC, WPF, Silverlight, UWP, Blazor, and now MAUI. Each iteration promises unification, abstraction, and better productivity over platform differences.
My first attempt at MAUI felt deceptively easy. I gave up shortly after.
So did the second attempt.
This time was different: I had a concrete delivery target. Giving up was no longer an option.
The Problem: A "Map-Like" Feature
The concrete requirement was simple to state but not easy to implement well: an app with a map-like feature. Not a full GIS stack, but something that looks and behaves like a map - large static textures, panning, zooming, overlays, and user interaction. No library - we are going to implement things from ground up.
My first instinct was to go cross-platform. After years of writing platform-locked GUIs that die the moment requirements change, I had no interest in repeating that mistake.
After surveying a wide range of options - inside and outside the C# ecosystem - I narrowed the choices down to:
- Unity
- Godot
- MAUI
I tried Unity first.
Unity: Smooth Onboarding, Rough Reality
Unity deserves credit where it's due. It was the first mobile platform I ever used seriously, and I had already developed an Android game with it years ago.
Coming back with significantly more experience in C#, general software engineering, and game development, the onboarding felt excellent. The editor is mature, the documentation is decent, and the mental model is comprehensible.
Within about three days, I felt comfortable again - comfortable enough to implement the prototype. The implementation itself was straightforward (details skipped here), but the result was not.
On an actual Android device, performance was terrible. Given time pressure, I didn't fully chase down the root cause. A few hours of profiling and hypothesis testing later, I made the call to bail. Whether the issue was overdraw, batching, UI layout, or just the wrong API for the task didn't matter anymore - the risk was too high. (Oh, did I mention the build size was gigantic?)
I would have liked to include some demo code - at least the Unity version - but time didn't allow for it. That will have to wait for another post or a future amendment.
MAUI: Dry, and That's a Compliment
Rewriting the same idea in MAUI worked. Not "worked eventually" - it worked immediately and predictably. (Actually I tried Expo before MAUI, which was very smooth, then I looked at the hundreds of dependencies, and quietly reconsidered)
That forced me to rethink a long-held assumption (something I had from the CG world): engines optimized for real-time rendering should be good at texture-heavy workloads. In practice, frameworks optimized for application UIs are often better at static or semi-static content - especially when the interaction model looks more like "pan and zoom" than "render 60 FPS no matter what."
MAUI was dry. I wasn't excited to see its "mobile-like" layout on a Windows screen, and I still am not.
But dry was exactly what the project needed.
The Hidden Cost
That said, this experience didn't magically make me like MAUI - and that reminds me of Blazor.
The core issue in both cases is structural. These frameworks attempt to abstract over domains whose constraints diverge in important, non-accidental ways. The abstraction holds only as long as the application stays within a relatively narrow envelope of complexity.
MAUI Issues
- Cross-platform inconsistencies are everywhere, not edge cases.
- iOS AoT compilation and trimming can cause things to silently stop working.
- Many behaviors that should be easily configurable (for example, disabling the Flyout) require platform-specific code.
- Debugging failures caused by trimming or linker behavior is disproportionately painful.
Blazor Issues
- Refactoring is far harder than it should be.
- Large component trees quickly become rigid.
- Abstractions that feel elegant early on tend to resist change later.
In both cases, the abstraction breaks - and when it does, you're often forced to drop down into platform-specific or framework-specific escape hatches that defeat the original promise.
These frameworks scale poorly in complexity, not because they are badly designed, but because they attempt to simplify domains that fundamentally resist unification. (Well, honestly in the case of MAUI I do think there are some design issues)
In general, MAUI is slightly better, because it's at least well encapsulated - just write platform-specific codes.
Some Takeaways
- Game engines are not automatically good UI frameworks. Application frameworks may outperform engines for static, interaction-heavy visuals. In this particular case I haven't tried Godot, but I suspect the same issue wouldn't happen to Godot.
- "Write once, run anywhere" really means "debug everywhere." And don't expect MAUI can save you effort because platform-specific behaviors are everywhere and it took me lots of time to back trace which seemingly trivial change many commits ago caused the entire application to break; Or, just turn off the magic
MtouchUseLlvm. - MAUI is an OK choice for simple cross-platform mobile apps in C#.
- Unified frameworks seem to trade short-term velocity for long-term friction. In the perfect world, I will use Swift for iOS.
In general, I wish framework developers make it clear their limitations so decision making can be made upfront.
Top comments (0)