Adding Razor cshtml view runtime re-compilation to a ASP.NET Core 5.0 app after creating it

I recently came across an interesting issue where after starting a new ASP.NET Core 5.0 .NET 5 project using the “ASP.NET Core Web App (Model-View-Controller)” template did not include the ability to update .cshtml Razor files without recompiling and restarting the whole app. There is a checkbox to “Enable Razor Runtime Compilation” during project setup but it’s easy to miss and tricky to add afterwards if you don’t know what you are looking for.

First, you need to head to the Nuget Package explorer by right clicking on the Dependencies node of your project tree selecting “Manage NuGet packages…”. Search for and install Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation. Install it and it’s dependencies into your project.

Then head to Startup.cs. Find the line within ConfigureServices() that says “services.AddControllersWithViews()” and add “.AddRazorRuntimeCompilation()”. You won’t be able to add this line without first installing the previous NuGet package.

Now run your app. You will see that changing the contents of Razor .cshtml files and refreshing the page will cause the view to be updated with your new content, without having to stop and recompile.

YouTube demonstration video

Razor Runtime Compilation - Reload Razor view in .NET Core 5.0 without recompiling
Watch this video on YouTube.

Handling and intercepting Back button Navigation in Xamarin Forms Shell

I’ve recently ended up needing to ask if the user really wants to navigate away from a page in my Xamarin app, Net Writer. Essentially whilst a post is being edited I don’t want the user to accidentally lose their progress, necessitating the need to inject a “Are you sure?” or “Confirm exit” prompt when the user presses either the Android hardware or OS level back button or the back button on the navigation bar provided by the Xamarin Forms Shell.

I found a blog post by Bohdan Benetskyi from March 2020, but it was geared for MvvmCross and not Xamarin Forms Shell. Here is how to adapt it.

Introducing IBackButtonHandler to your Page

If you have existing pages you can add an interface to them and implement it, for example here is a page from my app with IBackButtonHandler added to the class:

  [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PostListSummaryPage : ReactiveContentPage<PostSummaryListPageViewModel>, IBackButtonHandler
    {

This can then be implemented however you like, returning true if the back navigation should be aborted, for example:

public async Task<bool> HandleBackButton()
{
    if (await ShouldNotGoBack())
    {
        return true;
    }

    return false;
}

public async Task<bool> ShouldNotGoBack()
{
    var response = await this.DisplayActionSheet("Would you like to discard any unsaved changes to this post?", "No", "Yes, go back");

    return response == "No";
}

Once that is done next up is wiring up the back buttons.

Handling the Android OS back button

To handle the OS level back button you’ll need to add the following to MainActivity.cs:

public async override void OnBackPressed()
{
    var backButtonHandler = Shell.Current.CurrentPage as IBackButtonHandler;

    if (backButtonHandler == null)
    {
        base.OnBackPressed();
        return;
    }

    var backButtonHandled = await backButtonHandler.HandleBackButton();
    if (!backButtonHandled)
    {
        base.OnBackPressed();
    }
}

Lets step through this. First up, it attempts to cast the current Shell page as IBackButtonHandler. This means that only Pages that implement this interface will have any change of behaviour here. If the interface is not implemented, the base implementation of OnBackPressed() is called. If it is implemented, the HandleBackButton method is evaluated. If it returns true, the back navigation is cancelled. If false, the base implementation of OnBackPressed() is called, continuing the back navigation.

Handling the Shell back button

This is the button on the top left that shows when you are more than one level deep in the navigation stack. Handling this is less clean as it requires adding a call to Shell.SetBackButtonBehaviour in the constructor of each page that requires this. For example, add the following to the constructor of your page, after InitializeComponent():

Shell.SetBackButtonBehavior(this, new BackButtonBehavior()
{
    Command = new Command(async () => {

        var backButtonHandled = await this.HandleBackButton();
        if (!backButtonHandled)
        {
            await Navigation.PopAsync();
        }

    })
});

After that, you’l get a nice prompt pop up whenever you hit the OS back button or the shell navigation back button, but only on pages that implement IBackButtonHandler.

Walkthough video

If you would like a video walkthough of the above, check out this video:

How to confirm Xamarin Forms Shell Navigation Back Button
Watch this video on YouTube.

Using Google APIs and Auth in Xamarin Forms

I’m working on porting Net Writer from UWP to Android using Xamarin Forms. The Google authentication is a little bit tricky as it is constantly changing. Working off this amazing blog post by Timothé Larivière got me 90% of the way there but there are some updates to the process in 2020.

Pre-requisites to register an app with Google

At this point in time you’ll need to do the following before you can register a Public app:

  •  Create a project in GCP via the Google Developer Console
  •  Verify a domain via the Google Search Console. To do this you will need access to your nameserver and DNS records in order to copy and paste a TXT record. Access this here.
  •  Know the SHA-1 fingerprint of the key that will be used to sign your package

Getting the SHA-1 fingerprint used to sign a locally deployed debug Xamarin Forms app

You’ll need to do this:

  •  In Visual Studio, go to Tools > Android > Android Adb Command Prompt
  •  Navigate to C:\Users\{username}\AppData\Local\Xamarin\Mono for Android

Run the command

keytool -keystore debug.keystore -list -v

And when prompted enter the keystore password “android”.

You’ll see a result like this:

The SHA1 value is what you need.

Registering the application

You should have everything you need now. Go to Credentials in the GCP panel and create the OAuth consent screen. Fill in the details (you’ll need the verified domain you created earlier).

Then you can create an OAuth 2.0 Client ID. Select “Android” as the platform and enter the package name from AndroidManifest.xml and the SHA-1 fingerprint you figured out earlier. You’ll see something like the below:

Adding the code

The approach I have taken is to create a class in the main Android project to encapsulate everything, rather than putting the logic inside the Mobile/PCL project. This is because the Android version needs references to Activities and other Android-specific concepts to work effectively. It’s not just a case of adding Xamarin.Auth and calling a method unfortunately.

Using Xamarin Forms dependency injection I can refer to and call this class within the portable Mobile project when I need an access token from the API.

The token reader class

There are some nuget dependencies you’ll need for this – the “Google.Apis.Auth” libraries for the TokenResponse class (although you can probably remove the dependency from the below code if you’d like), “Xamarin.Auth”, “Xamarin.Auth.XamarinForms” and “Plugin.CurrentActivity”. The last one allows code outside of an Activity to get access to the current Activity.

    public class GoogleAccessTokenReader : IGoogleAccessTokenReader
    {
        public static readonly string[] GoogleAPIScopes =
        {
            DriveService.Scope.DriveFile,
            BloggerService.Scope.Blogger
        };

        public static TokenResponse Token { get; set; }

        public static OAuth2Authenticator Auth;

        public async Task<TokenResponse> GetOrNullAsync()
        {
            if (Auth == null)
            {
                Auth = new OAuth2Authenticator(
                "your-client-id",
                string.Empty,
                String.Join(" ", GoogleAPIScopes),
                new Uri("//accounts.google.com/o/oauth2/v2/auth"),
                new Uri("com.yourpackageid:/oauth2redirect"),
                new Uri("//www.googleapis.com/oauth2/v4/token"),
                isUsingNativeUI: true);

                Auth.Completed += OnAuthenticationCompleted;
            }

            if (Token != null) return Token;


            Xamarin.Auth.CustomTabsConfiguration.CustomTabsClosingMessage = null;

            var intent = Auth.GetUI(CrossCurrentActivity.Current.AppContext);

            CrossCurrentActivity.Current.Activity.StartActivity(intent);

            while(!Auth.HasCompleted)
            {
                await Task.Delay(500);
            }

            return Token;
        }

        private void OnAuthenticationCompleted(object sender, AuthenticatorCompletedEventArgs e)
        {
            if (e.IsAuthenticated)
            {
                Token = new TokenResponse()
                {
                    AccessToken = e.Account.Properties["access_token"],
                    TokenType = e.Account.Properties["token_type"],
                    Scope = e.Account.Properties["scope"],
                    ExpiresInSeconds = int.Parse(e.Account.Properties["expires_in"]),
                    RefreshToken = e.Account.Properties["refresh_token"]
                };

             }
         }

    }

Add the following dependency injection declaration to the namespace:

[assembly: Dependency(typeof(NetWriter.Mobile.Droid.GoogleAccessTokenReader))]

Where NetWriter.Mobile.Droid should be replaced with your namespace.

You can use this interface too as IGoogleAccessTokenReader (or change depending on your needs):

using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Responses;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace BlogWriter.Shared.NetStandard.Interfaces
{
    public interface IGoogleAccessTokenReader
    {
        Task<TokenResponse> GetOrNullAsync();
    }
}

Add the activity that receives the response

Somewhere in your main Android app you should add the following:

 [Activity(Label = "GoogleAuthInterceptor")]
    [IntentFilter(actions: new[] { Intent.ActionView },
              Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
              DataSchemes = new[]
              {
                  "com.yourpackageid"
              },
              DataPaths = new[]
              {
                   "/oauth2redirect"
              })]
    public class GoogleAuthInterceptor : Activity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            Android.Net.Uri uri_android = Intent.Data;

            var uri_netfx = new Uri(uri_android.ToString());

            GoogleAccessTokenReader.Auth?.OnPageLoading(uri_netfx);

            var intent = new Intent(this, typeof(MainActivity));
            intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
            StartActivity(intent);

            Finish();
        }
    }

The last three lines before Finish() are really important as they actually make the Google login window go away after logging in. if you don’t add them it will stay there and the user will need to manually close the window.

Retrieving the token from within your app

Using the Xamarin Forms dependency injection system, you can get an instance of the token reader like:

var reader = DependencyService.Get<IGoogleAccessTokenReader>();
var token = await reader.GetOrNullAsync();

Coming soon

Refresh tokens, remembering the login and other stuff. Oh my!

Demo video

If you want a walkthough of doing this, you can watch the following video:

How to add Google Login and Auth to Xamarin Forms
Watch this video on YouTube.