MaboaSoft Engineering Team

Xamarin.Native to .NET for iOS and .NET for Android: The Migration Nobody Writes About

Almost everything published about leaving Xamarin assumes you are on Forms and heading to .NET MAUI. If your app started as Xamarin.iOS or Xamarin.Android, that advice is wrong for you. Your destination is .NET for iOS and .NET for Android, your storyboards and AXML layouts carry over untouched, and there is no supported automated tooling. Here is what the job actually consists of — including the one breaking change that compiles cleanly and fails at runtime.

Xamarin.Native to .NET for iOS and .NET for Android: The Migration Nobody Writes About

Search for how to get off Xamarin and you will find a large, well-optimised body of content about migrating Xamarin.Forms to .NET MAUI. Custom renderers to handlers, XAML layout differences, the Upgrade Assistant, compiled bindings.

If your app started life as Xamarin.iOS or Xamarin.Android, essentially none of that applies to you.

You have no XAML layer to port. You have no custom renderer inventory. You are not going to MAUI. Your destination is .NET for iOS and .NET for Android, and it is a real job — just a completely different one, made harder by the fact that almost nobody writes about it.

This post is that missing article.

What you are actually moving to

Microsoft’s official position, from the Xamarin support policy:

Xamarin.Android, Xamarin.iOS, Xamarin.Mac are now integrated directly into .NET (starting with .NET 6) as .NET for Android, .NET for iOS, and .NET for Mac. If you’re building with these project types today, they should be upgraded to .NET SDK-style projects for continued support.

Note the phrasing: upgraded to .NET SDK-style projects. Not rewritten, not ported to a new UI framework. The migration hub is more explicit still:

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.

Your target framework moniker becomes net10.0-ios or net10.0-android. In .NET 10 those resolve to Android 36.0 and iOS 18.7 by default. (.NET 11 is in preview as of August 2026; its Android moniker appears in the preview release notes as net11.0-android37.)

UseMaui never appears. If someone on the project keeps steering toward MAUI, ask them which specific problem it solves — because the answer for a Native app is usually “none, and it adds a UI rewrite.”

What carries over untouched

This is the good news, and it is substantial.

iOS storyboards and xib files. The current .NET for iOS build-items reference still documents InterfaceDefinition as “an item group that contains interface definitions (_.xibor_.storyboard files)”. Also unchanged: ImageAsset, BundleResource, NativeReference.

Android AXML layouts and the whole Resources/ tree. Files with the AndroidResource build action are still compiled into Android resources. AndroidAsset and AndroidManifestOverlay carry forward too.

Your UIKit and Android view code. Microsoft’s own summary: “For most projects you won’t need to change namespaces or undertake other rewrites.”

So the UI — which in a Forms migration is the entire job — is close to free here. What you pay for instead is everything underneath it.

What the job actually consists of

1. The project system

Microsoft does not recommend converting the .csproj in place. The documented method:

To simplify the upgrade process, we recommend creating a new .NET project of the same type and name as your Xamarin native project, and then copy in your code.

Copy your code and resource files from the folders of your Xamarin native project to identical folders within your new app. You should overwrite any files of the same name.

Two housekeeping items that cause otherwise inexplicable build failures:

  • Delete every bin and obj folder before opening the solution, particularly when changing .NET versions.
  • Delete the generated Resource.designer.cs from the Android project.

2. MSBuild properties that were renamed, replaced, or removed

There are official tables for both platforms, and they are worth reading in full before you start. The load-bearing entries:

iOS / Mac

Xamarin property.NETAction
MtouchArchnoneConvert to one or more RuntimeIdentifiers
XamMacArchnoneConvert to RuntimeIdentifiers
HttpClientHandlerUseNativeHttpHandlerConvert
MtouchHttpClientHandlerUseNativeHttpHandlerConvert
CodeSigningKeyCodesignKeyRename
MtouchEnableSGenConcEnableSGenConcRename
MtouchLinkTrimModeDeprecated
MtouchExtraArgsAppBundleExtraOptionsDeprecated

MtouchArch maps to RIDs as: ARMv7 → ios-arm, ARM64 → ios-arm64, x86_64 → iossimulator-x64.

Also: MinimumOSVersion and LSMinimumSystemVersion move out of Info.plist and become SupportedOSPlatformVersion in the project file. Entitlements.plist is now picked up implicitly if it sits in the project root.

Android — these are not renames, they are unsupported values, and each one is a small piece of rework:

PropertySituation in .NET for Android
$(AndroidClassParser)class-parse only — jar2xml is not supported
$(AndroidDexTool)d8 only — dx is not supported
$(AndroidCodegenTarget)XAJavaInterop1 only — XamarinAndroid is not supported
$(DebugType)portable only — full and pdbonly are not supported
$(AndroidSupportedAbis)Should not be used — replace with <RuntimeIdentifiers>
$(AndroidUseLatestPlatformSdk)No longer supported; the build always targets the latest APIs
$(Mandroidl18n)Not supported — use the System.Text.Encoding.CodePages package

The <uses-sdk/> element should generally be removed from AndroidManifest.xml: TargetFramework now supplies android:targetSdkVersion and SupportedOSPlatformVersion supplies android:minSdkVersion, both injected at build time.

One behavioural change worth flagging to QA rather than to the build engineer: $(AndroidBoundExceptionType) now defaults to System, which Microsoft notes aligns exception types with .NET semantics “at the cost of compatibility with Xamarin.Android.” Exception-type-sensitive catch blocks are a real regression source here.

3. The breaking change that compiles cleanly and fails at runtime

If you take one thing from this article, take this one.

NSObject.Handle and INativeObject.Handle changed type from System.IntPtr to ObjCRuntime.NativeHandle. From Microsoft’s breaking-changes document for .NET for iOS:

One major caveat however is that user code with an IntPtr constructor … will have to be updated to … public MyViewController (NativeHandle handle) : base (handle) {}

Note that code that has the IntPtr constructor will compile just fine (no warnings nor errors), but it will fail at runtime.

Every view controller instantiated from a storyboard or a xib has that constructor. In a Xamarin.Native iOS app, that is most of your view controllers. The build will be green, the app will launch, and it will crash the moment the storyboard tries to instantiate a screen you did not open during smoke testing.

Grep for it before you do anything else:

grep -rn --include="*.cs" -e 'IntPtr handle' -e '(IntPtr ' . | grep -i -e 'base (handle)' -e ': base(handle)'

Then plan device testing that opens every storyboard-instantiated screen. This is the single highest-value QA decision in the whole migration.

4. The rest of the iOS breaking-change list

All from the same official document, all worth a targeted grep:

  • SDK assemblies renamedXamarin.iOS.dllMicrosoft.iOS.dll, and likewise for tvOS, Mac Catalyst and macOS. This breaks “code using reflection with hardcoded assembly name” and “custom linker configuration files, since they contain the assembly name.” Check every LinkDescription XML file you own.
  • System.nint / System.nuint removed — use nint / nuint, which map to IntPtr / UIntPtr. Code that overloads on both System.IntPtr and nint will no longer compile.
  • System.nfloat removed — replaced by System.Runtime.InteropServices.NFloat. nfloat.CopyArray is gone; use Buffer.CopyMemory.
  • SceneKit.SCNMatrix4 is now column-major“any transposing that’s currently done when accessing Apple APIs has to be undone.” Silent numerical breakage, no compiler help.
  • Foundation.MonoTouchExceptionObjCRuntime.ObjCException, System.NMathObjCRuntime.NMath, ObjCRuntime.Arch removed, CFHTTPStream and friends moved from CoreServices to CFNetwork.
  • CFNetwork.MessageHandler removed — use Foundation.NSUrlSessionHandler.

For Android there is no single equivalent breaking-changes document. The nearest official sources are the MSBuild property table above and the planned-deprecations wiki, which schedules $(AndroidCreatePackagePerAbi), $(AndroidUseIntermediateDesignerFile) and $(AndroidResgenFile) for removal in .NET 10.

5. Binding projects — where the Upgrade Assistant refuses to help

The .NET Upgrade Assistant “doesn’t support upgrading UWP projects, iOS extension projects, or binding projects.” Bindings are manual, and they are frequently the longest pole.

Objective-C bindings become a normal SDK-style project with <IsBindingProject>true</IsBindingProject>, ObjcBindingApiDefinition and ObjcBindingCoreSource item groups, and native libraries referenced via <NativeReference> with <Kind>Static</Kind>. The nint / nfloat / NativeHandle changes above land hardest here, because binding code is where those types are dense.

Objective Sharpie is still shipped and still documented — its docs were updated in March 2026 — so the tooling has not gone away.

Java bindings change shape more than they break. All .jar and .aar references consolidate onto <AndroidLibrary> with metadata, replacing EmbeddedJar, InputJar and AndroidJavaLibrary. class-parse and XAJavaInterop1 become mandatory, which can require rewriting hand-written binding code. Watch the default globbing — Transforms/*.xml and stray .jar files are auto-included, and teams routinely discover they have accidentally bound a Gradle wrapper. Exclude explicitly with <AndroidLibrary Remove="path/**/*" />.

6. Xamarin.Essentials, without MAUI

Remove the package, set <UseMauiEssentials>true</UseMauiEssentials>, call Platform.Init in your entry points, and swap namespaces:

OldNew
Xamarin.Essentials (app model)Microsoft.Maui.ApplicationModel
clipboard, file sharingMicrosoft.Maui.ApplicationModel.DataTransfer
battery, sensors, flashlightMicrosoft.Maui.Devices
media picking, text-to-speechMicrosoft.Maui.Media
file picking, secure storageMicrosoft.Maui.Storage

The Microsoft.Maui prefix is misleading — no MAUI UI framework is involved. UseMauiEssentials exists precisely so plain .NET for iOS and .NET for Android apps can consume this.

7. Two things that simply do not come with you

  • OpenGL on iOS is removed, because OpenTK is not available. If you have OpenGL rendering code, that is a rewrite to Metal, not a migration.
  • Xamarin.watchOS has no successor. Microsoft’s recommendation is bundling Swift extensions with .NET for iOS apps.

Sizing the work

We are not going to publish day counts for an app we have not seen — anyone who does is guessing. What we can give you is the set of countable inputs that actually drive the number, in descending order of impact.

DriverHow to count itWhy it dominates
Binding projectsgrep -rl 'ObjcBindingApiDefinition|AndroidClassParser' --include="*.csproj" .No tooling support, and the type-system changes concentrate here
Storyboard-instantiated controllersThe IntPtr handle grep aboveEach needs a constructor change; all need device testing
Incompatible NuGet packagesSee the compatibility table belowSome have no .NET-era successor and must be replaced or vendored
Custom linker configurationfind . -name "*.xml" | xargs grep -l 'linker'Assembly renames invalidate every hardcoded name
Native library referencesgrep -rn 'NativeReference|EmbeddedJar|LibraryProjectZip' --include="*.csproj" .Item-group restructuring plus re-validation on device
Screen countStoryboards, xibs, AXML layoutsDrives QA, not development

Package compatibility is a hard gate — these target frameworks have no path forward:

Incompatible: monotouch, xamarinios, xamarinios10, monomac, xamarinmac, xamarinmac20, xamarintvos, xamarinwatchos Compatible: monoandroid, monoandroidXX.X, and the net8.0-* / net9.0-* / net10.0-* platform monikers

Note the asymmetry: legacy Android packages generally keep working; legacy iOS packages do not. An iOS-heavy app with an old dependency list is meaningfully more expensive than an Android-heavy one with the same screen count.

The shape of the estimate, in relative terms: a small app with no bindings and a clean dependency list is a project-system exercise measured in days. An app with two or three binding projects, a custom linker configuration and a dense storyboard surface is measured in weeks, most of it spent on interop and device verification rather than on writing code. The variance between those two is driven almost entirely by the first two rows of that table, not by how big the app looks.

A note on the documentation

Microsoft’s Xamarin-native migration pages are stale. The native-projects, android-projects and apple-projects articles carry a February 2023 date and still reference net8.0-ios and net8.0-android. The reference documentation for .NET for Android and .NET for iOS is current and accurate.

Practical consequence: trust the migration docs for method and property tables, and the reference docs for version numbers and current defaults. Do not copy net8.0-* monikers out of the migration articles into a 2026 project.

How MaboaSoft helps

Xamarin.Native modernization is where our production experience is deepest — binding libraries, platform interop, and the kind of app where the UI is native and the complexity is underneath it. We take it on as a contained workstream so your team stays on the product roadmap rather than on a project-file archaeology exercise.

If you want a read on what your specific migration costs, book a 20-minute call. Bring your .csproj files and your dependency list — that is genuinely most of what we need.

FAQ

Do I have to move to .NET MAUI?

No. Microsoft’s migration guidance states that all projects do need to become SDK-style, that projects don’t need to be rewritten, and that multi-project solutions don’t need to become a multi-targeted single project. A Xamarin.iOS or Xamarin.Android app upgrades to .NET for iOS or .NET for Android, and UseMaui never enters the project file. MAUI is the destination for Xamarin.Forms, not for Native.

Do my storyboards and AXML layouts survive the migration?

Yes. .NET for iOS still uses the InterfaceDefinition build item for .xib and .storyboard files, and .NET for Android still compiles the Resources tree through the AndroidResource build action. Microsoft states that for most projects you won’t need to change namespaces or undertake other rewrites. You do have to delete the generated Resource.designer.cs before rebuilding.

Does the .NET Upgrade Assistant migrate Xamarin.Native projects?

Not as a Native-to-.NET tool. Its supported project types list includes Xamarin Forms and .NET MAUI, not standalone Xamarin.iOS or Xamarin.Android, and it explicitly does not support binding projects. It will convert the iOS and Android head projects of a Forms solution, but it does so while setting UseMaui to true. For a standalone Native app, Microsoft’s documented approach is manual.

What is the most dangerous breaking change?

The NSObject handle type change. NSObject.Handle and INativeObject.Handle moved from System.IntPtr to ObjCRuntime.NativeHandle. Microsoft’s own breaking-changes document warns that code with an IntPtr constructor will compile with no warnings and no errors, but will fail at runtime. This affects every view controller instantiated from a storyboard or xib, which in a Xamarin.Native app is most of them.

Can I keep using Xamarin.Essentials?

Not the package, but the functionality has a supported path that does not require MAUI. Remove the Xamarin.Essentials NuGet package, set UseMauiEssentials to true in the project file, call Platform.Init in your platform entry points, and replace the Xamarin.Essentials namespace with the relevant Microsoft.Maui namespaces such as Microsoft.Maui.Devices and Microsoft.Maui.Storage. The UI framework is not involved.


Resources

All property tables, breaking changes and target framework monikers verified against the sources above on 6 August 2026. .NET 11 is in preview; treat its monikers as provisional.