MaboaSoft Engineering Team

Xamarin.Forms or Xamarin.Native? Which Migration You Are Actually Facing

Teams inheriting a legacy .NET mobile app frequently do not know which Xamarin flavour they have — and the two lead to completely different migrations, with different destinations, different tooling and different costs. Getting it wrong costs a quarter. This is a five-minute diagnostic with runnable commands that settles it definitively, then routes you to the right path.

Xamarin.Forms or Xamarin.Native? Which Migration You Are Actually Facing

Someone hands you a repository. It is a .NET mobile app, it was built between 2016 and 2022, nobody who wrote it still works there, and you have been asked what it costs to modernise.

Before you can answer that, you have to answer a smaller question that a surprising number of teams get wrong: which Xamarin is this?

It matters more than it sounds. Xamarin.Forms and Xamarin.Native are not two versions of the same thing. They have different destinations, different tooling support, different work profiles and different cost curves. Scoping a Native app as though it were Forms — or the reverse — is how a migration estimate ends up wrong by a factor rather than a percentage.

This takes five minutes to settle. Here is how.

The two-minute version

Xamarin.FormsXamarin.Native
Where the UI livesShared XAML in a common project.storyboard / .xib (iOS), Resources/layout/*.axml (Android)
Destination.NET MAUI.NET for iOS / .NET for Android
Automated tooling.NET Upgrade Assistant supports itNo supported automated path
The big work itemCustom renderers → handlers, layout behaviour changesProject system, bindings, MSBuild properties, interop
The thing you do not have to doRewrite the UI

The last row is the one that changes budgets. A Xamarin.Native migration does not touch your storyboards or your AXML layouts. They carry over. That is a very different conversation from a Forms migration, where the UI layer is the work.

The diagnostic

Run these from the repository root. The order matters — detect Forms first, because a Forms solution contains Xamarin.iOS and Xamarin.Android projects, and starting from the platform projects will mislead you.

Step 1 — Is there Xamarin.Forms anywhere?

# Package reference (the definitive marker)
grep -rn --include="*.csproj" --include="packages.config" \
  -e 'Xamarin\.Forms' -e 'Xamarin\.Essentials' .

# Forms XAML namespace
grep -rln --include="*.xaml" 'xamarin.com/schemas/2014/forms' .

# Forms bootstrap calls in the platform heads
grep -rn --include="*.cs" \
  -e 'Forms\.Init' -e 'LoadApplication' \
  -e 'FormsApplicationDelegate' -e 'FormsAppCompatActivity' .

PowerShell equivalent:

Get-ChildItem -Recurse -Include *.csproj,packages.config |
  Select-String -Pattern 'Xamarin\.Forms|Xamarin\.Essentials'

Get-ChildItem -Recurse -Include *.xaml |
  Select-String -Pattern 'xamarin.com/schemas/2014/forms' -List

Any hits across all three? You have Xamarin.Forms. Skip to Reading the result.

Nothing at all? Continue to step 2 and confirm what you do have.

Step 2 — Confirm Xamarin.iOS

Legacy Xamarin.iOS projects carry a project type GUID and a targets import that nothing else uses:

grep -rni --include="*.csproj" \
  -e 'FEACFBD2-3405-455C-9665-78FE426C6842' \
  -e '6BC8ED88-2882-458C-8E55-DFD12B67127B' \
  -e 'Xamarin\.iOS\.CSharp\.targets' \
  -e 'Xamarin\.MonoTouch\.CSharp\.targets' .

The -i matters. A GUID is case-insensitive, and the casing of <ProjectTypeGuids> varies between hand-edited, template-generated and Visual Studio-written project files. A case-sensitive search returns nothing on a project that is plainly Xamarin.iOS.

FEACFBD2-… is the Unified API — what you will almost certainly find. 6BC8ED88-… is the Classic MonoTouch API, and if you find that, you have an app that predates 2016 and a considerably larger job.

The cleanest Xamarin-only property tells, because they have no .NET equivalent at all:

grep -rn --include="*.csproj" -e 'MtouchArch' -e 'XamMacArch' .

File-level confirmation — Main.cs calling UIApplication.Main, an AppDelegate.cs, an Info.plist, and the UI itself:

find . \( -name "*.storyboard" -o -name "*.xib" \) | head -30

Step 3 — Confirm Xamarin.Android

# Project type GUID — case-insensitive, as above
grep -rni --include="*.csproj" 'EFBA0AD7-5A72-4C68-AF49-83D382785DCF' .

# Properties that are unsupported in .NET for Android — unambiguous legacy markers
grep -rn --include="*.csproj" \
  -e 'AndroidUseLatestPlatformSdk' \
  -e 'AndroidSupportedAbis' \
  -e 'jar2xml' \
  -e '<AndroidDexTool>dx</AndroidDexTool>' \
  -e 'XamarinAndroid' .

# The UI, and the generated file you will need to delete during migration
find . -path "*/Resources/layout/*.axml" | head -30
find . -name "Resource.designer.cs"

Note the file extension: Android itself uses .xml for layouts, but Xamarin.Android uses .axml. Seeing .axml is a reliable signal on its own.

A caveat worth stating: the iOS project type GUID is documented by Microsoft. The Android one is not — it is however visible in Microsoft’s own repositories, and it is consistent across every Xamarin.Android project we have inspected. Treat it as strong evidence rather than an official specification, and confirm with the property and file markers alongside it.

Step 4 — Score the split

If step 1 found Forms and steps 2–3 found substantial native UI, you have a hybrid. Measure it rather than guessing:

echo "XAML pages:      $(find . -name '*.xaml' | wc -l)"
echo "iOS storyboards: $(find . \( -name '*.storyboard' -o -name '*.xib' \) | wc -l)"
echo "Android layouts: $(find . -path '*/Resources/layout/*.axml' | wc -l)"
echo "Custom renderers:$(grep -rl --include='*.cs' 'ExportRenderer' . | wc -l)"

Reading the result

Forms with a handful of storyboards or layouts. Normal. Every Forms app has a launch screen and a splash. This is a Forms migration; the native files are noise. Your work is the XAML layer, the custom renderers, and the layout behaviour changes.

Forms with a heavy custom renderer count. Still a Forms migration, but the renderer inventory is the cost driver, not the page count. Renderers are where the Forms→MAUI work actually lives, and there is now a deadline attached to them — see You Migrated Xamarin.Forms to .NET MAUI and the App Got Slower.

No Forms, storyboards and AXML present. Xamarin.Native. Your destination is .NET for iOS and .NET for Android, and it is a genuinely different job — no UI rewrite, but a full pass over the project system, MSBuild properties, binding libraries and interop. That path is covered in Xamarin.Native to .NET for iOS and .NET for Android.

Both, substantially. Two migrations wearing one repository. Sequence them: bring the whole solution onto SDK-style projects and current .NET first, and only then decide what happens to the UI layer. Trying to do both at once is how these projects stall.

Classic MonoTouch GUIDs, or packages.config rather than PackageReference. You are further back than the rest of this article assumes. Start with The Legacy Repository Discovery Checklist — you have an archaeology problem before you have a migration problem.

Why the wrong answer costs a quarter

Two specific ways teams lose time here, both of which we have seen.

Scoping a Native app as a Forms app. The team estimates a MAUI migration, budgets for a UI rewrite, and discovers three weeks in that there is no XAML to port — but also that the .NET Upgrade Assistant will not help them, because Xamarin.iOS and Xamarin.Android are not supported standalone project types and the tool refuses binding projects outright. The estimate was wrong in both directions at once: the UI work does not exist, and the tooling assistance does not either.

Assuming MAUI is the destination. This is the more expensive error, because it produces a project that succeeds at the wrong thing. Microsoft’s own migration guidance is explicit:

All projects do need to become SDK-style. Projects don’t need to be rewritten. Multi-project solutions don’t need to become a multi-targeted single project.

A Xamarin.Native app can move to .NET for iOS and .NET for Android and stop there. UseMaui never enters the picture. Adding a MAUI rewrite on top converts a mechanical migration into a product rebuild, and the business case usually does not survive contact with that number.

Both errors come from the same root: somebody answered “which Xamarin is this?” from memory or from the folder names, rather than from the project files. Five minutes of grep prevents both.

What to do with the answer

Write it down, with evidence. Whoever is funding this will ask, and “we checked the project files and found no Xamarin.Forms reference, no shared XAML, and 47 storyboards” is an answer that survives scrutiny. “It’s Native, I think” is not.

Then pick the right path:

How MaboaSoft helps

We run this diagnostic as the first hour of any legacy .NET mobile engagement, because everything downstream depends on getting it right. If you would rather not do the archaeology yourself, we will run it against your repository and give you the classification, the evidence, and a scoped effort band — before anyone commits to a number.

Book a 20-minute call.

FAQ

How do I tell if my app is Xamarin.Forms or Xamarin.Native?

Look for a shared project containing XAML pages and a Xamarin.Forms package reference. Xamarin.Forms solutions have a shared library holding the UI, plus thin iOS and Android head projects that only call Forms.Init and LoadApplication. Xamarin.Native has no shared UI layer at all — the interface lives in .storyboard and .xib files on iOS, and in Resources/layout/*.axml on Android. If you find no XAML and no Xamarin.Forms package reference, you have Native.

Does a Xamarin.Forms solution contain Xamarin.iOS and Xamarin.Android projects?

Yes, and this is the single most common source of confusion. A Forms solution always includes iOS and Android head projects that are technically Xamarin.iOS and Xamarin.Android projects. Their presence does not make the app Native. Detect Forms first by looking for the shared library and XAML; only if those are absent is the app genuinely Xamarin.Native.

Is .NET MAUI the destination for both?

No. Xamarin.Forms upgrades to .NET MAUI. Xamarin.Native upgrades to .NET for iOS and .NET for Android, which do not require MAUI at all. Microsoft’s migration guidance is explicit that projects do not need to be rewritten and that multi-project solutions do not need to become a multi-targeted single project. Treating MAUI as the automatic destination for a Native app adds a UI rewrite that nobody asked for.

What if the codebase is a mix of both?

Mixed codebases are common in long-lived apps — a Forms shell with substantial native modules, custom renderers wrapping platform controls, or one platform partly rewritten natively. Score the split by counting XAML pages against native view files. If native code is a small minority you have a Forms migration with native follow-up work; if it is a third or more, you are effectively running two migrations and should plan them separately.

Does the .NET Upgrade Assistant handle both?

No. The Upgrade Assistant supports Xamarin.Forms projects and upgrades them toward .NET MAUI, setting UseMaui to true. Xamarin.iOS and Xamarin.Android are not listed as standalone supported project types, and the tool explicitly does not support binding projects. For a standalone Xamarin.Native app there is no supported automated path — Microsoft’s documented approach is manual.


Resources

Commands and markers verified against the sources above on 6 August 2026.