How to migrate Xamarin to MAUI: Convert your project with our migration guide

Converting your existing Xamarin.Forms app to MAUI isn’t trivial, but we’re here to help! In this blog post, you’ll learn how to prepare your project for migration – and how to transition the Scanbot SDK to the framework.

Yurii April 30, 2024 10 mins read
app store

On May 1st, 2024, Xamarin officially rides into the sunset, leaving us developers waving goodbye to a veteran framework. At the same time, we celebrate the rise of .NET Multi-platform App UI (MAUI).

MAUI is more than just an evolution of Xamarin – it’s a game-changer. From improved performance and new capabilities to sleek native UI features, MAUI has a lot to offer.

Chapter 1: Migration madness

🧑‍🚀 Strap in, Xamarin survivor! You’re about to launch into the MAUIverse!

Preparing for takeoff

To successfully transition your existing Xamarin project to .NET MAUI, there are a few essential steps you should follow:

  1. Project conversion: Convert your project from the .NET framework to the .NET SDK style. This lays the groundwork for compatibility with .NET MAUI.
  2. Namespace updates: Update the namespaces within your codebase to align with the new .NET MAUI namespaces. This ensures coherence and prevents conflicts.
  3. NuGet package management: Update or remove any NuGet packages incompatible with .NET MAUI. Additionally, remove redundant packages.
  4. Handling breaking changes: Address any breaking API changes that may occur during the migration process. This ensures that your application functions seamlessly post-migration.

Transition strategies

Developers have two main transition strategies to choose from: You can either use Microsoft’s Upgrade Assistant or do a manual upgrade.

Option 1: .NET Upgrade Assistant

The Upgrade Assistant is designed to streamline the transition to .NET MAUI. While it’s not fully baked yet, it aims to simplify the migration process, and offers tips and tricks along the way.

Here is James Montemagno, Technical Program Manager at Microsoft, demonstrating the tool with practical examples:

You can also use the CLI to interact with the Upgrade Assistant.

To install this .NET tool, use the following global install command:

dotnet tool install -g upgrade-assistant

Next, run the following for the project you want to port:

upgrade-assistant upgrade /path/to/your/project

Once you’ve upgraded your project, it’s important to test it thoroughly. It’s likely that you’ll need to fix errors that arose during the upgrade process.

Option 2: Manual upgrade

If you’re feeling hands-on, you can go the manual route. This involves adding the Xamarin.Forms code files, resources, and dependencies to the new MAUI project, then making the necessary modifications to remove Xamarin.Forms-specific code or dependencies.

The DIY approach offers more control over the migration process – but also requires careful attention to detail.

Typical manual migration example:

Turn on this track for a morale boost while you’re tackling manual migration: La Caution – “Thé à la Menthe” (or as we like to call it: The Laser Dance Song)

Quick tips 

We recommend creating a new .NET MAUI project and gradually migrating your codebase. The Upgrade Assistant can be helpful, but it’s clear that it’s still in its early stages.

Think of it like moving your belongings to a new house one box at a time. Instead of trying to relocate everything in one hectic day, you move a few items each trip. 

The gradual approach ensures that each piece of code is carefully moved and tested before you continue with the next.

Based on our own experience with migrating an example project to .NET MAUI, we have some practical advice to offer:

  1. Managing dependencies is a critical consideration during the transition to .NET MAUI, especially given the varying support for packages. Replacing unsupported packages with suitable alternatives takes time and careful research. Stay patient and thoroughly explore available package options, or alternatively, consider using custom binding libraries.
  2. Utilize partial classes in .NET MAUI to segregate platform-specific code rather than dependency registration. This enhances code organization and maintainability.
  3. You can still rock Xamarin.Forms custom renderers in your .NET MAUI app rather than rewriting them into handlers, but they may not fully leverage the capabilities and optimizations provided by the latter. Therefore, a balanced approach that uses both custom renderers and handlers depending on the specific use case and performance considerations can be an effective strategy.

For the nitty-gritty details of how to move your Xamarin.Forms app to the .NET SDK style, visit Microsoft’s official documentation.

Chapter 2: Transitioning the Scanbot SDK

🚀 Ready to hop aboard? Here’s how you bring the Scanbot SDK along.

Now, let’s take a closer look at the changes involved in switching to the .NET MAUI version of our SDK. 

During the transition from Xamarin to .NET MAUI, our familiar APIs underwent an evolution, some changing more than others. In the following comparison table, we’ll dissect these changes, showing how our most-used functionalities have been adapted to the .NET MAUI ecosystem.

Most-used APIs and their usage compared: ScanbotSDK.Xamarin.Forms vs. ScanbotSDK.MAUI:

Xamarin signatures and usageMAUI signatures and usage
Signature:

Task<DocumentScannerResult> LaunchDocumentScannerAsync(DocumentScannerConfiguration configuration = null);

Usage:

SBSDK.UI.LaunchDocumentScannerAsync(configuration);
Signature:

Task<DocumentScannerResult> LaunchDocumentScannerAsync(DocumentScannerConfiguration configuration = null);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchDocumentScannerAsync(configuration)
Signature:

Task<DocumentQuality> DetectDocumentQualityAsync(ImageSource source);

Usage:
var quality = await SBSDK.Operations.DetectDocumentQualityAsync(await page.DecryptedDocument());
Signature:

Task<DocumentQuality> DetectDocumentQualityAsync(ImageSource source);

Usage:

var quality = await ScanbotSDK.MAUI.ScanbotSDK.SDKService.DetectDocumentQualityAsync(await page.DecryptedDocument());
Signature:

Task<ImageSource> Pick();

Usage:

ImagePicker.Forms.ImagePicker.Instance.Pick();
Signature:

Task<ImageSource> PickImageAsync();

Usage:

ScanbotSDK.MAUI.ScanbotSDK.PickerService.PickImageAsync();
Signature:

Task<IScannedPage> CreateScannedPageAsync(ImageSource source);

Usage:

SBSDK.Operations.CreateScannedPageAsync(source);
Signature:

Task<IScannedPage> CreateScannedPageAsync(ImageSource source);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.SDKService.CreateScannedPageAsync(source);
Signature:

Task<BarcodeScanningResult> LaunchBarcodeScannerAsync(BarcodeScannerConfiguration configuration = null);

Usage:

SBSDK.UI.LaunchBarcodeScannerAsync(config);
Signature:

Task<BarcodeResultBundle> OpenBarcodeScannerView(BarcodeScannerConfiguration configuration);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.OpenBarcodeScannerView(config);
Signature:

Task<BatchBarcodeScanningResult> LaunchBatchBarcodeScannerAsync(BatchBarcodeScannerConfiguration configuration = null);

Usage:

SBSDK.UI.LaunchBatchBarcodeScannerAsync(config);
Signature:

Task<BarcodeResultBundle> OpenBatchBarcodeScannerView(BatchBarcodeScannerConfiguration configuration);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.OpenBatchBarcodeScannerView(config);
Signature:

Task<List<Barcode>> DetectBarcodesFrom(ImageSource image);

Usage:

SBSDK.Operations.DetectBarcodesFrom(source);
Signature:

Task<List<Barcode>> DetectBarcodesFrom(ImageSource image);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.DetectionService.DetectBarcodesFrom(source);
Signature:

Task<MrzScannerResult> LaunchMrzScannerAsync(MrzScannerConfiguration configuration = null);

Usage:

SBSDK.UI.LaunchMrzScannerAsync(configuration);
Signature:

Task<MrzScannerResult> LaunchMrzScannerAsync(MrzScannerConfiguration configuration = null);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchMrzScannerAsync(configuration);
Signature:

Task<HealthInsuranceCardScannerResult> LaunchHealthInsuranceCardScannerAsync(HealthInsuranceCardConfiguration configuration = null);

Usage:

SBSDK.UI.LaunchHealthInsuranceCardScannerAsync(configuration);
Signature:

Task<HealthInsuranceCardScannerResult> LaunchHealthInsuranceCardScannerAsync(HealthInsuranceCardConfiguration configuration = null);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchHealthInsuranceCardScannerAsync(configuration);
Signature:

Task<GenericDocumentRecognizerResult> LaunchGenericDocumentRecognizerAsync(GenericDocumentRecognizerConfiguration configuration = null);

Usage:

SBSDK.UI.LaunchGenericDocumentRecognizerAsync(configuration);
Signature:

Task<GenericDocumentRecognizerResult> LaunchGenericDocumentRecognizerAsync(GenericDocumentRecognizerConfiguration configuration = null);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchGenericDocumentRecognizerAsync(configuration);
Signature:

Task<CheckRecognizerResult> LaunchCheckRecognizerAsync(CheckRecognizerConfiguration configuration = null);

Usage:

SBSDK.UI.LaunchCheckRecognizerAsync(configuration);
Signature:

Task<CheckRecognizerResult> LaunchCheckRecognizerAsync(CheckRecognizerConfiguration configuration = null);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchCheckRecognizerAsync(configuration);
Signature:

Task<TextDataScannerResult> LaunchTextDataScannerAsync(TextDataScannerConfiguration configuration);

Usage:

SBSDK.UI.LaunchTextDataScannerAsync(configuration);
Signature:

Task<TextDataScannerResult> LaunchTextDataScannerAsync(TextDataScannerConfiguration configuration);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchTextDataScannerAsync(configuration);
Signature:

Task<VINScannerResult> LaunchVINScannerAsync(VINScannerConfiguration? configuration = null);

Usage:

SBSDK.UI.LaunchVINScannerAsync(configuration);
Signature:

Task<VINScannerResult> LaunchVINScannerAsync(VINScannerConfiguration configuration = null);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchVINScannerAsync(configuration);
Signature:

Task<Uri> CreatePdfAsync(IEnumerable<ImageSource> images, PDFPageSize pageSize);

Usage:

SBSDK.Operations.CreatePdfAsync(Pages.Instance.DocumentSources, PDFPageSize.A4);
Signature:

Task<Uri> CreatePdfAsync(IEnumerable<FileImageSource> files, PDFPageSize pageSize = PDFPageSize.Custom, PDFPageOrientation pdfPageOrientation = PDFPageOrientation.Auto, OcrConfig configuration = null);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.SDKService.CreatePdfAsync(Model.Pages.Instance.DocumentSources.OfType<FileImageSource>(), PDFPageSize.A4);
Signature:

Task<OcrResult> PerformOcrAsync(IEnumerable<ImageSource> images, OcrConfigs configs = null, string pdfOutputPath = null);

Usage:

SBSDK.Operations.PerformOcrAsync(Pages.Instance.DocumentSources, SBSDK.Operations.OcrConfigs, pdfFilePath);
Signature:

Task<OcrResult> PerformOcrAsync(IEnumerable<FileImageSource> files, TiffOptions options = null);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.SDKService.PerformOcrAsync(Model.Pages.Instance.DocumentSources.OfType<FileImageSource>(), ScanbotSDK.MAUI.ScanbotSDK.SDKService.DefaultOcrConfig, pdfFilePath);
Signature:

Task<Uri> WriteTiffAsync(IEnumerable<ImageSource> images, TiffOptions options = null);

Usage:

SBSDK.Operations.WriteTiffAsync(Pages.Instance.DocumentSources, new TiffOptions { OneBitEncoded = true, Dpi = 300, Compression = TiffCompressionOptions.CompressionCcittT6 });
Signature:

Task<Uri> WriteTiffAsync(IEnumerable<FileImageSource> files, TiffOptions options = null);

Usage:

ScanbotSDK.MAUI.ScanbotSDK.SDKService.WriteTiffAsync(Model.Pages.Instance.DocumentSources.OfType<FileImageSource>(), new TiffOptions { OneBitEncoded = true, Dpi = 300, Compression = TiffCompressionOptions.CompressionCcittT6 });
Signature:

Task CleanUp();

Usage:

SBSDK.Operations.CleanUp();
Signature:

Task CleanUp();

Usage:

ScanbotSDK.MAUI.ScanbotSDK.SDKService.CleanUp();

We have manually migrated Scanbot.SDK.Example.Forms project to MigrationForms. Take a look: https://github.com/doo/scanbot-sdk-example-xamarin-forms/tree/migration-net8-maui

While the transition did bring changes to the SDK’s structure, usage patterns, and initialization, the alterations for the most commonly used APIs were relatively minor. 

Renaming namespaces and making slight adjustments to method signatures hasn’t meaningfully changed how the core SDK functionalities are utilized. Nevertheless, moving to the MAUI SDK does entail some code updates.

In-depth customization: Meet our Classic Components

Classic Components are the latest addition to ScanbotBarcode.SDK.MAUI. You can use them to create custom screens with unmatched flexibility and customization options. The Classic Components come with a variety of easy-to-integrate UI elements, all designed to seamlessly embed into your app and extend your unique screens.

XAML and C# namespace mappings for the BarcodeScannerView and BarcodeScanAndCountView controls in Xamarin and .NET MAUI.:

CONTROLXAML NAMESPACE and USAGEC# NAMESPACE
BarcodeScannerViewxmlns:classicComponent="clr-namespace:ScanbotSDK.MAUI.ClassicComponent;assembly=BarcodeSDK.MAUI" />

Usage:

<classicComponent:BarcodeScannerView x:Name="cameraView" OnBarcodeScanResult="CameraView_OnBarcodeScanResult" OnSelectBarcodeResult="CameraView_OnSelectBarcodeResult"
using ScanbotSDK.MAUI.ClassicComponent;
BarcodeScanAndCountViewxmlns:classicComponent="clr-namespace:ScanbotSDK.MAUI.ClassicComponent;assembly=BarcodeSDK.MAUI" />

Usage:

<classicComponent:BarcodeScanAndCountView x:Name="scanAndCountView" OnBarcodeScanResult="CameraView_OnBarcodeScanResult" OnScanAndCountFinished="CameraView_OnScanAndCountFInished"
using ScanbotSDK.MAUI.ClassicComponent;

BaseBarcodeScannerView

Bindable Properties:

Property NameType
OverlayConfigurationPropertySelectionOverlayConfiguration
IsFlashEnabledPropertybool
FinderConfigurationPropertyFinderConfiguration
CameraZoomRangePropertyZoomRange
CameraZoomLevelPropertyfloat?
BarcodeFormatsPropertyList<BarcodeFormat>
AcceptedDocumentFormatsPropertyList<BarcodeDocumentFormat>
MinFocusDistanceLockPropertybool
CameraModulePropertyCameraModule

BarcodeScanAndCountView

Bindable Properties:

Property NameType
CheckMarkImageByteArrayPropertybyte[]

Events:

Event NameType
StartDetectionHandlerEventHandler<EventArgs>
StartScanAndCountHandlerEventHandler<EventArgs>
ContinueScanningHandlerEventHandler<EventArgs>
StopDetectionHandlerEventHandler<EventArgs>
OnBarcodeScanResultBarcodeScannerResultHandler
OnScanAndCountFinishedOnScanAndCountFinishedHandler

BarcodeScannerView

Bindable Properties:

Property NameType
OverlayTextEnabledPropertybool

Events:

Event NameType
OnResumeHandlerEventHandler<EventArgs>
OnPauseHandlerEventHandler<EventArgs>
StartDetectionHandlerEventHandler<EventArgs>
StopDetectionHandlerEventHandler<EventArgs>
OnBarcodeScanResultBarcodeScannerResultHandler
OnSelectBarcodeResultSelectBarcodeResultHandler

We highly recommend exploring projects that leverage the Classic Components, like our example app. They allow you to use the SDK more creatively and efficiently, ultimately enhancing the overall quality of your application.

Wrapping up the migration journey

It’s only fair to acknowledge the challenges we’ve faced along the way. Let’s be honest – migration can be a pain. It’s time-consuming, often frustrating, and sometimes feels like an uphill battle. 

However, despite the hurdles, we’ve endeavored to make your transition as smooth as possible, at least when it comes to the Scanbot SDK.

Our team has worked tirelessly to spare you unnecessary pain and frustration. While we can’t promise that migration will ever be completely painless, we hope that our efforts make your journey a little less arduous.

So, as we turn the page on this chapter, let’s carry forward and look ahead to brighter horizons with renewed optimism and determination.

📚 .NET MAUI migration resources

  1. Live example of migrating ScanbotSDK.Xamarin → MAUI: https://github.com/doo/scanbot-sdk-example-xamarin-forms/tree/migration-net8-maui
  2. ScanbotSDK.MAUI example: https://github.com/doo/scanbot-sdk-maui-example
  3. ScanbotBarcodeSDK.MAUI example: https://github.com/doo/scanbot-barcode-sdk-maui-example
  4. The Scanbot SDK docs: https://docs.scanbot.io/
  5. Microsoft Docs: Upgrading from Xamarin to .NET: https://learn.microsoft.com/en-us/dotnet/maui/migration/?view=net-maui-8.0
  6. .NET Upgrade Assistant for Visual Studio: https://www.youtube.com/watch?v=7EaHKGUCIqc