0

I am working on a Xamarin.iOS project, and have a MVVMCross value converter that takes a file extension string and returns the corresponding file icon as an UIImage.

This converter is used in a file list. I found that when I'm scrolling the list, as soon as the MvxTableViewCell is recycled, the UI converter is often invoked in the background thread for the recycled cell, making my code to throw an error complaining calling a UIKit method from a background thread.

It looks like in the latest version of MVVMCross, the old IMvxMainThreadDispatcher.RequestMainThreadAction method is obsolete. The documentation recommends to use IMvxMainThreadAsyncDispatcher.ExecuteOnMainThreadAsync method.

However, this method returns a Task with no generic type. This obviously can't work with the Convert method of the value converter which expects the UIImage.

Is there a way to configure MVVMCross to always invoke the converter on UI thread?

Update:

Take the following code example:

public class FileIconConverter : MvxValueConverter<string, UIImage>
{
    protected override UIImage Convert(string fileExtension, Type targetType, object parameter, CultureInfo culture)
    {
        // When this is called, it could be in a background thread.
        // Since UIDocumentInteractionController.FromUrl requires to be called
        // on the UI thread, it would throw the UIKitThreadAccessException.
        return UIDocumentInteractionController.FromUrl(NSUrl.FromFilename("/tmp/generic" + fileExtension)).Icons[0];
    }
}
Leon Zhou
  • 633
  • 6
  • 20

1 Answers1

1

Since you have not added any code here all I can do is give you a bit of speculative advice.

What you can do is use the InvokeOnMainThread method of Xamarin.iOS

When you check the Xamarin.iOS documentation on Working with the UI Thread in Xamarin.iOS

You can see that you use this method to make the changes that are supposed to be made on the UI

InvokeOnMainThread ( () => {
// manipulate UI controls
});

Update

First of all, if you check the code for IMvxMainThreadDispatcher.RequestMainThreadAction at the MvvmCross's Git it is internally calling the same API!

So what you can do to your existing code is something like

public class FileIconConverter : MvxValueConverter<string, UIImage>
{
protected override UIImage Convert(string fileExtension, Type targetType, object parameter, CultureInfo culture)
  {
    UIImage image;
    IMvxMainThreadAsyncDispatcher.ExecuteOnMainThreadAsync(()=>{

    image=UIDocumentInteractionController.FromUrl
               (NSUrl.FromFilename("/tmp/generic" + fileExtension)).Icons[0];


  });
    return image;
 }
}
FreakyAli
  • 13,349
  • 3
  • 23
  • 63
  • Hey thanks for your advice. The problem I'm facing is actually inside the value converter. I have updated my question to include some sample code to better illustrate the issue. – Leon Zhou Sep 03 '19 at 11:11
  • The problem with `InvokeOnMainThread` method is that, 1 it's not available in the value converter context because it is a method in the `NSObject` class, and 2 it won't be able to return the value in the `Convert` method in the value converter. – Leon Zhou Sep 03 '19 at 11:19
  • 1
    You can use the `UIApplication.SharedApplication` to use the method, any way Check my updated answer in some time – FreakyAli Sep 03 '19 at 12:01
  • Thanks! Both your suggestions work. I ended up using `UIApplication.SharedApplication.InvokeOnMainThread`, just in case MVVMCross updates the behaviour of `ExecuteOnMainThreadAsync` to be truly asynchronous in the future. Although I'm a bit concerned on the performance since it causes a thread switch for each item in my file list. – Leon Zhou Sep 03 '19 at 23:37
  • Well, it's still better than being synchronous so I guess that is fine. Anyway, good luck and feel free to get back in case of issues. – FreakyAli Sep 04 '19 at 05:47