Complete CI/CD tutorial for Xamarin Android with Google Play publish in Azure DevOps | Part 1.

This tutorial will drive you through setting up a great CI/CD pipeline for Xamarin Android in a fully hosted Azure DevOps enviroment.

Part 1 contains:

  • Creating an empty Xamarin.Android build pipeline
  • Uploading keystore file to secure files

Start with some code

An Initial repository

I have added some basic code to my demo repository. It is a boilerplate Xamarin Application, with no additional customized code. If you have code in your repo, make sure it builds successfully.

Create your first pipeline

On the left side menu, go to Pipelines/Pipelines. This menu will show up a welcome page, to create new pipeline.

Let’s do it!

Click on ‘Create Pipeline’ button, or if you have already created your first pipeline, find a button to add a new one.

On the next page, a wizard will guide you through the basic setup.

Point for your repository

If you have your code in Azure Repos, click the button for that.

Select your repo

Select your repository where your Xamarin Android code lives.

Select Xamarin.Android Template

On the next page, you can select a template to create your pipeline yml. Let’s choose Xamarin.Android.

Rename your yml file if you want

If you want, you can rename your yml file. Pipeline files will be placed in your repository root by default. YML file extension stands for YAML files. Review your newly created file, how it looks like. Luckily, you do not have to write yaml too much, but good to know, how it markups the build process.

Lets save the YAML

Okay it is enough for now, we have some things to do outside of the pipeline editor. Go save your configuration.

Create a new branch for that

Create a new branch for the pipeline setup with name like pipeline_configuration. You can directly push the config to the main branch, but in this step of the tutorial, i recommend to create a branch for that. Later on, we will set branch policies 😉

Store your app signing key securely

Your app signing key is a very important file to keep your binaries trusted for your users. You can provide with signing, that the binary has been built by you, and not by a bad guy. Keeping this file secure is a must have thing.

Azure DevOps pipelines have a library function, where you can store your custom agent connection settings, and files needed to build or sign your applications securely.

Jump to library

Let’s jump into library menu. Select secure files option, and upload your keystore file.

Creating a keystore in Visual Studio for Mac

If you do not have a keystore, you can create a new one when you are trying to publish a Xamarin.Android application as AdHoc. Keep your Alias, Password in mind, you will need this in the future.

Location of release keystores on Mac

The location of the release keystores on Mac is:

~/Library/Developer/Xamarin/Keystore/

Once, you have uploaded your keystore to the secure file storage, you need to set the permissions, to pipelines in order to access your secure files. Note your secure file filename, you will need this too in the future. Click the three dots on the uploaded file, and select edit.

Set permissions for pipelines

Check the pipeline permissions checkbox, and save your file.

In the next session

We will continue set up CI to our Android project.

Go to Part 2 to continue configuring your repository

This content has 3 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

Xamarin.Forms: Reopening application best pratices

If you have experienced that reopening your application from the phone’s app drawer or launcher is fails because it crashes your application, or displays the first set main page again instead of displaying the latest one, keep reading this article.

If you have crashes, or malfunctions, you are probably initializing something in the constructor of the App.xaml.cs, or in the overridden method called ‘OnStart’ like this:

        public App()
        {
            InitializeComponent();
            InitalizeOnlyOnceClass.Initalize();
        }

        protected override void OnStart()
        {
            AnAnotherInitalizeOnlyOnceClass.Initalize();
        }

Imaginary InitalizeOnlyOnceClass’s Initialize method can be called only once, for the second call, it throws an exception. After you have started your program, sent to background, and bringing it back throws the second Initalization’s exception. This is because the App class gets constructed again when reopening application from the drawer.

To handle this, you should make a boolean for the application’s initialization progress.

        private static bool isInitalized;

        public App()
        {
            InitializeComponent();

            if(isInitalized == false)
            {
                InitalizeOnlyOnceClass.Initalize();
            }
        }

If you receive a blank white screen, you are probably forget to set the Main Page

Make sure, that the Application.Current.MainPage always gets a value.
If you have implemented the Initialization by your self, or an another way, make sure you have handled correctly the else statement also.
Even if the application has been initialized once, the Application.Current.MainPage have to be set always when reconstructing the App.

Continue with the last page opened in the application

If you want to continue always with the last page opened, you need to store the last page always, when you are navigating from one to an another.

You can store the last page with making a class used for navigation, like this:

    public static class SimpleNavigationLogic
    {
        private static Xamarin.Forms.Page lastNavigatedPage;

        public static void ChangeMainPage(Xamarin.Forms.Page pageToSet)
        {
            Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
            {
                Application.Current.MainPage = pageToSet;
            });
            lastNavigatedPage = pageToSet;
        }

        public static void RestoreMainPage()
        {
            Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
            {
                Application.Current.MainPage = lastNavigatedPage;
            });
        }
    }

Than you can make the initialization for your application like this:

        public App()
        {
            InitializeComponent();

            if(!isInitalized)
            {
                InitalizeOnlyOnceClass.Initalize();
                SimpleNavigationLogic.ChangeMainPage(new AwesomeMainPage());
            }
            else
            {
                SimpleNavigationLogic.RestoreMainPage();
            }
        }
This content has 3 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

Xamarin: How .NET MAUI will change the life of Xamarin.Forms development

.NET 6 and .NET MAUI is on the sill, but how will it affect the development of Xamarin applications?

For today, I have brought you a video by James Montemagno, a Xamarin developer working at Microsoft.

Follow James on the following platforms:

? https://montemagno.com
? https://mergeconflict.fm, https://blunders.fm, https://nintendodispatch.com
? https://twitter.com/jamesmontemagno
? https://youtube.com/jamesmontemagno

This content has 3 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

Xamarin.Android : Google Play app target level error solution

If you are uploading an application to the Google Play, and it gives the following error:

Your app currently targets API level 28 and must target at least API  level 29 to ensure it is built on the latest APIs optimized for security  and performance. Change your app's target API level to at least 29 

You have to change the target framework and the target android version in your androidmanifest.xml or in the UI editor of the manifest. To change it, set the Target Framework in the properties tab of your android application

Changing target framework

Then Navigate to Android Manifest option, and set target android version

Setting Target Android version

If you are re archive your application, it will upload successfully.

This content has 3 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

Xamarin.Forms: Bindable Property snippet for Visual Studio

Use bprop tab tab to generate commands in ViewModels with this snippet

The result of the snippet

Save the snippet file as {name}.snippet in Visual Studio’s folder: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC#\Snippets\1033\Visual C#

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
		<Header>
			<Title>Bindable Property</Title>
			<Shortcut>bprop</Shortcut>
			<Description>Xamarin BindableProperty declaration code snippet</Description>
			<Author>banditoth.hu</Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal>
					<ID>PropertyName</ID>
					<ToolTip>The property's name</ToolTip>
					<Default>Foo</Default>
				</Literal>
				<Literal>
					<ID>Type</ID>
					<ToolTip>The type of the property</ToolTip>
					<Default>object</Default>
				</Literal>
				<Literal>
					<ID>DefaultValue</ID>
					<ToolTip>The default value of the property</ToolTip>
					<Default>null</Default>
				</Literal>
				<Literal>
					<ID>BindingMode</ID>
					<ToolTip>The binding mode of the property</ToolTip>
					<Default>TwoWay</Default>
				</Literal>
				<Literal>
					<ID>PropertyChangedHandler</ID>
					<ToolTip>The property changed handler method</ToolTip>
					<Default>null</Default>
				</Literal>
				<Literal>
					<ID>PropertyChangingHandler</ID>
					<ToolTip>The property changing handler method</ToolTip>
					<Default>null</Default>
				</Literal>
			</Declarations>
			<Code Language="csharp">
				<![CDATA[
        public static readonly BindableProperty $PropertyName$Property = BindableProperty.Create(
                                        propertyName: nameof($PropertyName$),
                                        returnType: typeof($Type$),
                                        declaringType: typeof(View),
                                        defaultValue: $DefaultValue$,
                                        defaultBindingMode: BindingMode.$BindingMode$,
                                        propertyChanged: $PropertyChangedHandler$,
					propertyChanging: $PropertyChangingHandler$);

        public $Type$ $PropertyName$
        {
            get { return ($Type$)GetValue($PropertyName$Property); }
            set { SetValue($PropertyName$Property, value); }
        }
		$end$]]>
			</Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>
This content has 3 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.