Xamarin Essentials MediaPicker not working on iOS

Viewed 429

I'm using Xamarin Essentials MediaPicker (v1.7.3) on my Xamarin Forms 5 app and it just freezes and eventually crashes the app on iOS but works perfectly fine on Android.

My app uses the MVVM pattern so I have an ImageButton that's wired to a method in my view model and the code for that method looks like this:

async Task Add_Image_Tapped()
{
   try
   {
      var photosPermission = await Permissions.CheckStatusAsync<Permissions.Photos>();
      var cameraPermission = await Permissions.CheckStatusAsync<Permissions.Camera>();

      if (photosPermission != PermissionStatus.Granted || photosPermission != PermissionStatus.Restricted)
         photosPermission = await Permissions.RequestAsync<Permissions.Photos>();

      if (cameraPermission != PermissionStatus.Granted || cameraPermission != PermissionStatus.Restricted)
         cameraPermission = await Permissions.RequestAsync<Permissions.Camera>();

      if ((photosPermission == PermissionStatus.Granted
         || photosPermission == PermissionStatus.Restricted)
         && (cameraPermission == PermissionStatus.Granted
         || cameraPermission == PermissionStatus.Restricted))
         await HandleImagePicker();
   }
   catch(Exception e)
   {
      throw new Exception(e.Message);
   }
}

And if the permissions check out fine, here's the method that handles the image pick up:

private async Task HandleImagePicker()
{
   try
   {
      var filePath = string.Empty;
      var fileName = string.Empty;

      MainThread.BeginInvokeOnMainThread(() => {

         var result = MediaPicker.PickPhotoAsync(new MediaPickerOptions
         {
            Title = "Pick Image"
         }).Result;

         if (result == null)
            return;

         filePath = result.FullPath;
         fileName = result.FileName;

      });

      if (!string.IsNullOrEmpty(filePath) && !string.IsNullOrEmpty(fileName))
      {
         // Do something with the image
      }
   }
   catch(Exception e)
   {
      throw new Exception(e.Message);
   }
}

I'm using Xamarin Forms 5.0.0.2401, Xamarin Community Toolkit 2.0.2 and Xamarin Essentials 1.7.3. I also have the following permissions in my Info.plist:

<key>NSCameraUsageDescription</key>
<string>MyApp would like to access your camera</string>
<key>NSMicrophoneUsageDescription</key>
<string>MyApp would like to access your microphone</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>MyApp would like to access your photo library</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>MyApp would like to access your photo library</string>

I also initialize Xamarin Essentials in AppDelegate FinishedLaunching() method as below:

Xamarin.Essentials.Platform.Init(() => new UIViewController());

Any idea what maybe the issue here?

UPDATE:

The above code reflects the current code I have after making changes based on suggestions such as using the main thread.

Currently, if I run the app on iOS Simulator on a Mac, it literally hangs forever. Never throws an exception or crashes.

If I run it on a real device i.e. iPhone Xs, it will hang for quite a while and eventually crash the whole app.

As mentioned earlier, it all works perfectly fine on Android.

3 Answers

Can you show how you are requesting for user permissions to access the device storage.

Suggestion - add a check method for permissions check for every time you try to access the device storage. You can use Permissions.CrossPermissions plugin for it.

An sample for it would look like -

async Task Image_Picker_Tapped()
{
   if (await GetPhotoLibraryPermission())
   {
      var result = await MediaPicker.PickPhotoAsync(new MediaPickerOptions
      {
         Title = "Pick Image"
      };
   }
   // Handle the image
}
private async Task<bool> GetPhotoLibraryPermission()
        {
            PermissionStatus cameraPermissionStatus = await CrossPermissions.Current.CheckPermissionStatusAsync<CameraPermission>();
            if (cameraPermissionStatus != PermissionStatus.Granted)
            {
                cameraPermissionStatus = await CrossPermissions.Current.RequestPermissionAsync<CameraPermission>();
            }
            var storagePermissionStatus = await CrossPermissions.Current.RequestPermissionAsync<StoragePermission>();
            if (storagePermissionStatus != PermissionStatus.Granted)
            {
                storagePermissionStatus = await CrossPermissions.Current.RequestPermissionAsync<StoragePermission>();
            }
            return cameraPermissionStatus == PermissionStatus.Granted && storagePermissionStatus == PermissionStatus.Granted;
        }

Here are some suggestions

  1. Try to revert the method signature to the original one .

    private async void Image_Picker_Tapped(object sender, EventArgs e){}
    
  2. Wrap your code with Try Catch to see if there is any error information.

  3. Do not set any parameter while calling PickPhotoAsync .

    var result = await MediaPicker.PickPhotoAsync();
    
  4. Try to add await Task.Delay(100) before calling PickPhotoAsync.

Its important to use PickPhotoAsync with await.

This code probably traded your original hang for a different hang:

 var result = MediaPicker.PickPhotoAsync(new MediaPickerOptions
 {
    Title = "Pick Image"
 }).Result;

You need to switch to MainThread and start an async context.
Given that you are already in an async method, use a newer MainThread method, InvokeOnMainThreadAsync. AND add async inside the Invoke:

// Have "await" and "async" here.
await Device.InvokeOnMainThreadAsync(async () => {
    ...
    // HAVE "await" here.
    var result = await MediaPicker.PickPhotoAsync(new MediaPickerOptions
    {
        Title = "Pick Image"
    });   // DON'T use ".Result".

YES await/async, NO .Result.


If this still hangs, then please make a public github repo, containing a simple project that shows the hang, that can be built and run.

Related