How to Create an MRZ Scanner App for Android and iOS with .NET MAUI
Creating a mobile app that scans and decodes Machine Readable Zones (MRZs) is essential for many industries, including travel, security, and identification verification. With .NET MAUI (Multi-platform App UI), developers can build cross-platform applications for Android and iOS using a single codebase, streamlining development and maintenance. In this tutorial, we’ll demonstrate how to leverage .NET MAUI alongside the Dynamsoft Capture Vision MAUI Bundle to create an MRZ scanner app with ease.
This article is Part 3 in a 3-Part Series.
.NET MAUI MRZ Scanner Demo Video
Prerequisites
To get started, ensure you have the following tools installed:
- Dynamsoft Capture Vision Trial License
- Xcode
- Android Studio
- Visual Studio for Windows or Visual Studio Code for macOS with the .NET MAUI extension
- Provisioning Profile for iOS (required if building a .NET MAUI app with Visual Studio Code on macOS):
- Log into your Apple Developer account.
- Navigate to Certificates, Identifiers & Profiles to create an App ID and Provisioning Profile.
- Download the
*.mobileprovision
file and copy it to the~/Library/MobileDevice/Provisioning Profiles
folder.
Getting Started with the Official MRZ Scanner Sample
Dynamsoft’s GitHub page offers several .NET MAUI samples, including those for barcode reading, document normalization, and MRZ recognition.
To get started, clone the MRZ MAUI sample project from the GitHub repository:
git clone https://github.com/Dynamsoft/mrz-scanner-mobile-maui.git
This project is configured for .NET 7.0
. To update the target framework to .NET 8.0
:
- Open the
.csproj
file and change theTargetFramework
value fromnet7.0
tonet8.0
. - Add
<SupportedOSPlatformVersion>
to thePropertyGroup
element to ensure compatibility:<PropertyGroup Condition="$(TargetFramework.Contains('-ios'))"> <SupportedOSPlatformVersion>11.0</SupportedOSPlatformVersion> </PropertyGroup>
The sample project includes basic MRZ scanning functionality. You can run the app on Android and iOS devices to test the MRZ recognition feature. In the following sections, we will examine the MRZ scanner app in detail and explain how it works.
Anatomy of the .NET MAUI MRZ Scanner App
The MRZ scanner app consists of three pages: MainPage, ScanPage, and ResultPage. The MainPage is the entry point of the app, where users can initiate the MRZ scanning process. The ScanPage performs real-time MRZ scanning using the camera stream, while the ResultPage displays the MRZ information extracted from the recognized MRZ strings.
MainPage
The MainPage contains a button that navigates to the ScanPage.
MainPage.xml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage NavigationPage.HasNavigationBar="False"
NavigationPage.HasBackButton="False"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Title=""
BackgroundColor="#323234"
x:Class="mrz_scanner_mobile_maui.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="25"
VerticalOptions="Start"
Padding="30,130">
<Button
x:Name="NavigationBtn"
Text="Scan an MRZ"
FontSize="16"
WidthRequest="180"
BackgroundColor="#FE8E14"
Clicked="OnNavigationBtnClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
MainPage.xaml.cs
namespace mrz_scanner_mobile_maui;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void OnNavigationBtnClicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new ScanPage());
}
}
ScanPage
The ScanPage starts a camera preview with the CameraView
control provided by the Dynamsoft Capture Vision SDK. Here’s the ScanPage.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
Shell.NavBarIsVisible="False"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Dynamsoft.CameraEnhancer.Maui;assembly=Dynamsoft.CameraEnhancer.Maui"
Title=""
x:Class="mrz_scanner_mobile_maui.ScanPage">
<AbsoluteLayout>
<controls:CameraView x:Name="camera"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All">
</controls:CameraView>
...
</AbsoluteLayout>
</ContentPage>
The CameraView
is connected to a CameraEnhancer
object, which serves as the input source for the CaptureVisionRouter
object. The CaptureVisionRouter
processes the camera stream and recognizes MRZ strings, returning results through callback events.
Here’s the code snippet from the ScanPage.xaml.cs file:
-
Set the license key for the Dynamsoft Capture Vision SDK and instantiate
CameraEnhancer
andCaptureVisionRouter
in the constructor.public ScanPage() { InitializeComponent(); LicenseManager.InitLicense("LICENSE-KEY", this); currentTemplate = "ReadPassportAndId"; enhancer = new CameraEnhancer(); router = new CaptureVisionRouter(); }
-
Bind the
CameraView
to theCameraEnhancer
object:protected override void OnHandlerChanged() { base.OnHandlerChanged(); if (this.Handler != null) { enhancer.SetCameraView(camera); enhancer.EnableEnhancedFeatures(EnumEnhancedFeatures.EF_FRAME_FILTER); } }
-
Register callback events for MRZ recognition and start camera capturing in the
OnAppearing
event handler:void OnParsedResultReceived(ParsedResult result) { if (result.Items == null) { return; } ImageData data = router.GetIntermediateResultManager().GetOriginalImage(result.OriginalImageHashId); Dictionary<String, String> labelMap = AssembleMap(result.Items[0]); if (labelMap != null && labelMap.Count != 0) { MainThread.BeginInvokeOnMainThread(() => { Navigation.PushAsync(new ResultPage(labelMap, data)); ClearText(); }); } } protected override async void OnAppearing() { base.OnAppearing(); beepStatus = Preferences.Default.Get("status", true); UpdateBackground(); await Permissions.RequestAsync<Permissions.Camera>(); MultiFrameResultCrossFilter filter = new MultiFrameResultCrossFilter(); filter.EnableResultCrossVerification(EnumCapturedResultItemType.CRIT_TEXT_LINE, true); router?.AddResultFilter(filter); try { router.SetInput(enhancer); } catch (Exception e) { e.GetBaseException(); } router.AddResultReceiver(this); restartCapture(); enhancer?.SetColourChannelUsageType(EnumColourChannelUsageType.CCUT_FULL_CHANNEL); enhancer?.Open(); }
When a recognized result is returned, a corresponding image is stored in the
CaptureVisionRouter
object. By default, the image is converted to grayscale. To retain the original color, callSetColourChannelUsageType
method withCCUT_FULL_CHANNEL
before starting camera capture.
ResultPage
The ResultPage displays the MRZ information extracted from the recognized MRZ strings in a ScrollView
.
The ResultPage.xaml file is as follows:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="mrz_scanner_mobile_maui.ResultPage"
BackgroundColor="#3B3B3B"
Title="ResultPage">
<ScrollView x:Name="ScrollView">
<VerticalStackLayout x:Name="VerticalLayout"
Padding="30">
</VerticalStackLayout>
</ScrollView>
</ContentPage>
The ResultPage.xaml.cs file populates the VerticalStackLayout
with MRZ information extracted from recognized MRZ strings. It also displays an image of the scanned document.
namespace mrz_scanner_mobile_maui;
using Dynamsoft.Core.Maui;
public partial class ResultPage : ContentPage
{
public ResultPage(Dictionary<String, String> labelMap, ImageData imageData)
{
InitializeComponent();
if (labelMap.Count > 0)
{
VerticalLayout.Add(ChildView("Document Type:", labelMap["Document Type"]));
VerticalLayout.Add(ChildView("Document Number:", labelMap["Document Number"]));
VerticalLayout.Add(ChildView("Full Name:", labelMap["Name"]));
VerticalLayout.Add(ChildView("Sex:", labelMap["Sex"].First().ToString().ToUpper()));
VerticalLayout.Add(ChildView("Age:", labelMap["Age"]));
VerticalLayout.Add(ChildView("Issuing State:", labelMap["Issuing State"]));
VerticalLayout.Add(ChildView("Nationality:", labelMap["Nationality"]));
VerticalLayout.Add(ChildView("Date of Birth(YYYY-MM-DD):", labelMap["Date of Birth(YY-MM-DD)"]));
VerticalLayout.Add(ChildView("Date of Expiry(YYYY-MM-DD):", labelMap["Date of Expiry(YY-MM-DD)"]));
var imageControl = new Image
{
HeightRequest = 400,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
};
imageControl.Source = imageData.ToImageSource();
VerticalLayout.Add(imageControl);
}
}
IView ChildView(string label, string text)
{
return new VerticalStackLayout
{
new Label
{
Text = label,
TextColor = Color.FromArgb("AAAAAA"),
FontSize = 16,
Padding = new Thickness(0, 20, 0, 0),
},
new Entry
{
Text = text,
TextColor = Colors.White,
FontSize = 16,
BackgroundColor = Colors.Transparent,
IsReadOnly = false,
}
};
}
}
Running the .NET MAUI MRZ Scanner
-
Scan the machine-readable zone (MRZ) on a passport or ID card.
-
Extract MRZ information from the scanned document.
Source Code
https://github.com/yushulx/maui-barcode-mrz-document-scanner/tree/main/examples/MrzScanner