Monday, September 11, 2017

WebView blank in Xamarin Forms on UWP

TLDR;

If you are hooking up events for a Xamarin Forms WebView, wrap the hooking up in a Device.BeginInvokeOnMainThread if you experience a blank WebView. I've seen this issue mostly on UWP.

    public MyView()
    {
        this.InitializeComponent();
       
        Device.BeginInvokeOnMainThread(() =>
        {
            // Hook up events in here
            this.myWebView.Navigating += async (s, e) =>
            {
                // Do stuff here
            };
        });
    }

Longer version

I have a login view in my app and the code looks like this.

    public partial class LoginView : ContentPage
    {
        public LoginView(LoginViewModel vm)
        {
            this.InitializeComponent();
            this.BindingContext = vm;

            this.LoginWebView.Navigating += async (s, e) =>
            {
                this.BusyLabel.IsVisible = true;
                await vm?.Navigating(e.Url);
            };

            this.LoginWebView.Navigated += async (s, e) =>
            {
            
                this.BusyLabel.IsVisible = false;
            };
  
        }
    }

I needed to hook up the Navigating and Navigated events for two reasons;
  1. To display a busy/loading label over the webview
  2. To pass the resulting URL back to the ViewModel
This works... Most of the time. But sometimes the WebView just displays a white square. I tried a bunch of different urls trying to determine if it was something in the html that was messing something up. 

Then I read this post (https://forums.xamarin.com/discussion/63280/xamarin-forms-webview-does-not-work-once-the-navigating-eventhandler-is-setup). It wasn't really about this issue but the title of the post made me think.

Perhaps, if I delay the event hooking-uping it might magically work again. 

So I wrapped the hookups in a Device.BeginInvokeOnMainThread(...) call and it just seems to work.

An other related issue you might experience is this. But this goes for all controls, not only WebView.
  • https://stackoverflow.com/questions/34330446/xamarin-forms-webview-not-showing-up


Sunday, September 10, 2017

Mapping without Automapper

I've tried AutoMapper on numerous occasions and it's a great product. However, my mind seems to work a little bit different so I usually use a little different approach. I want to be able to follow my mappings so what I end up doing is to write the mappers by hand. (#bad)

I do give AutoMapper a try from time to time, but I still favour this way of doing it. It's just a personal preference!

If you haven't heard of Automapper, check it out at http://automapper.org/ before you embrace this technique! :)

The pattern I use for this is simple (and the magic lies in the simplicity of it).

Step 0 - Define the types on each side of the fence


Investigate the types you want to map. I have two ducks.

    public class BackendDuck
    {
        public int Id { getset; }
        public int BeakSize { getset; }
        public string TotallySecretDuckKey { getset; }
    }

    public class FrontEndDuck
    {
        public int Id { getset; }
        public int BeakSize { getset; }
    }

Step 1 - Create the mapper class


I create a static Mapper class that will handle my mappings.

   public static partial class Mapper
   {
   }


Why partial? Because I like to keep the option open to create another file to append to this mapper class. The file on disk could be called DuckMapper.cs. Then why isn't the class called DuckMapper.cs? Because we really don't want to check fifteen different classes to find the correct mapper. The answer is within step 2.

Step 2 - create the map methods:


Create two methods, both called Map (this is important), with the signature below.

    public static partial class Mapper
    {
        public static IEnumerable<FrontEndDuck> Map(IEnumerable<BackendDuck> source)
        {
            return null;
        }

        public static FrontEndDuck Map(BackendDuck source)
        {
            return null;
        }
    }

We always want two methods, one to Map an IEnumerable and one to map an item.

Step 3a - implement the map methods (non-linq way):


The first way of implementing the methods is by not using Linq. It's the straight forward, C# way to do it. No surprises at all.

    public static partial class Mapper
    {
        public static IEnumerable<FrontEndDuck> Map(IEnumerable<BackendDuck> source)
        {
            var result = new List<FrontEndDuck>();

            foreach(var item in source)
            {
                result.Add(Map(item));
            }

            return result;
        }

        public static FrontEndDuck Map(BackendDuck source)
        {
            return new FrontEndDuck()
            {
                Id = source.Id,
                BeakSize = source.BeakSize
            };
        }
    }

Step 3b - implement the map method using Linq


This does the same thing, but the looping part is hidden in a .Select extension method.

    public static partial class Mapper
    {
        public static IEnumerable<FrontEndDuck> Map(IEnumerable<BackendDuck> source)
        {
            return source.Select(x => Map(x));
        }

        public static FrontEndDuck Map(BackendDuck source)
        {
            return new FrontEndDuck()
            {
                Id = source.Id,
                BeakSize = source.BeakSize
            };
        }
    }

Step 4 - usage


This is how you would use the Mapper.

    public class DuckManager
    {
        public void DoStuff()
        {
            // Create a single duck
            var backendDuck = new BackendDuck() 
            { 
                Id = 1
                BeakSize = 42
                TotallySecretDuckKey = "TheKey" 
            };

            // Add the duck to a list
            var backendDucks = new List<BackendDuck>()
            {
                backendDuck
            };

            // Use the mappings - if setup correctly, 
            // all you have to remember is Mapper.Map(...)
            var item = Mapper.Map(backendDuck);
            var list = Mapper.Map(backendDucks);
        }
    }

Summary


Pros:
  • It's easy to follow debugging
  • You get all your mappings written in C# code that you can follow, nothing is hidden
  • Written correctly, just remember Mapping.Map( ) and you are set
Cons:
  • More code to write

Resources


Check out Automapper if you haven't done so. It might fit your needs better.

Saturday, September 9, 2017

RFLCT_Ambiguous when running UWP in release and using Autorest

This is one of those short stories...

TLDR;


If you get a RFLCT_Ambigous exception in your UWP app in Release configuration on the initialization of the autogenerated service client that Autorest created for you then simply update the Microsoft.NETCore.UniversalWindowsPlatform package.

The issue does not arise in debug mode.

Long version


I'm ashamed... I spent way to much time on this issue before I tried what I should've tried the first time I saw the error. 

Always update your nuget-packages before trying to solve an issue related to some other persons code. Oh, and always do so in a feature/bug branch or at least make sure you can roll stuff back. Things can go bad.

The issue at hand by the way was that some Attributes had been duplicated. And if you try to get an attribute by doing something like this:

 GetCustomAttribute(typeof(AssemblyFileVersionAttribute))

It might actually return multiple attributes. And a piece of code inside Microsoft.Rest.ClientRuntime didn't like this. It seems like attributes that makes no sense duplicating has been duplicated, like AssemblyFileVersion... Why would you like to have two of those.

Anyhow... I'm going to write a couple of hours of my clients bill now and tattoo "always update nuget packages first" on a close by colleague so I will remember it in the future.


Resources / sources


https://github.com/Azure/autorest/issues/1542

https://github.com/aspnet/EntityFrameworkCore/issues/6242

https://github.com/dotnet/corefx/issues/16769

Tuesday, September 5, 2017

Injecting a new root element in a Xamarin Forms Content Page

If you want to insert a new root element on a content page (using Xamarin Forms) you must set the Content to null first before assigning it again. Otherwise touch events will not register (at least not on iOS).

For example, add the entire view in an AbsoluteLayout could look like this:

 // Get a reference to the content
 var content = page.Content;
 page.Content = null// This line is key

 // Create a new root element and add the content
 var absoluteLayout = new AbsoluteLayout();
 absoluteLayout.Children.Add(content);

 // Replace the content
 page.Content = absoluteLayout;

I would do this directly after the call to InitializeComponent() in the code behind of the view.