XamarinCommunityToolkit: [Bug] [CameraView] [iOS] Taking photos doesn't work on iOS using Hot Restart

Description

I’m using Hot Restart in Windows to debug my Xamarin.Forms app on iOS. Whenever I take a photo (trigger the ShutterCommand), the app UI freezes and the MediaCaptured event never gets called.

I also get this lovely exception:

=================================================================
	Basic Fault Address Reporting
=================================================================
Memory around native instruction pointer (0x1010ae8c4):0x1010ae8b4  a8 83 5e f8 09 00 80 52 09 01 00 39 a8 03 5f f8  ..^....R...9.._.
0x1010ae8c4  09 01 40 39 69 0f 00 34 a8 03 5f f8 09 01 c0 39  ..@9
.

=================================================================
	Managed Stacktrace:
=================================================================

.4.._....9
0x1010ae8d4  29 8d 00 71 e8 03 09 aa 08 7d 40 d3 1f 69 01 f1  )..q.....}@..i..
0x1010ae8e4  e8 13 00 f9 68 0b 00 54 08 00 00 90 08 61 2c 91  ....h..T.....a,
	  at <unknown> <0xffffffff>
	  at UIKit.UIApplication:UIApplicationMain <0x000b8>
	  at UIKit.UIApplication:Main <0x00020>
	  at UIKit.UIApplication:Main <0x00038>
	  at XctCameraApp01.iOS.Application:Main <0x00012>
	  at <Module>:runtime_invoke_direct_void_string[] <0x0008c>
	  at <unknown> <0xffffffff>
	  at System.Reflection.RuntimeMethodInfo:InternalInvoke <0x00030>
	  at System.Reflection.RuntimeMethodInfo:Invoke <0x000fc>
	  at System.Reflection.MethodBase:Invoke <0x0001a>
	  at Xamarin.PreBuilt.iOS.Applications:Main <0x00254>
	  at <Module>:runtime_invoke_direct_void_string[] <0x00092>
=================================================================

Steps to Reproduce

  1. CameraPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="XctCameraApp01.MainPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:xct="http://xamarin.com/schemas/2020/toolkit">

    <Grid>
        <xct:CameraView
            x:Name="cameraView"
            CaptureMode="Photo"
            OnAvailable="cameraView_OnAvailable">
            <xct:CameraView.Behaviors>
                <xct:EventToCommandBehavior Command="{Binding PhotoCapturedCommand}" EventName="MediaCaptured" />
            </xct:CameraView.Behaviors>
        </xct:CameraView>
        <Button
            Margin="20"
            Command="{Binding ShutterCommand, Source={x:Reference cameraView}}"
            HorizontalOptions="Center"
            IsEnabled="{Binding CanTakePhoto}"
            Text="Take photo"
            VerticalOptions="End" />
    </Grid>

</ContentPage>
  1. CameraPage.xaml.cs
using Xamarin.Forms;

namespace XctCameraApp01
{
    public partial class MainPage : ContentPage
    {
        private readonly MainViewModel _model;

        public MainPage()
        {
            InitializeComponent();
            BindingContext = _model = new MainViewModel();
        }

        private void cameraView_OnAvailable(object sender, bool e)
        {
            _model.CanTakePhoto = e;
        }
    }
}
  1. MainViewModel.cs
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.CommunityToolkit.UI.Views;
using Xamarin.Essentials;
using Xamarin.Forms;
using static Xamarin.Essentials.Permissions;

namespace XctCameraApp01
{
    public class MainViewModel : ObservableObject
    {
        private bool _canTakePhoto;
        public bool CanTakePhoto
        {
            get => _canTakePhoto;
            set => SetProperty(ref _canTakePhoto, value);
        }

        public ICommand PhotoCapturedCommand => new AsyncCommand<MediaCapturedEventArgs>(PhotoCapturedAsync);

        private async Task PhotoCapturedAsync(MediaCapturedEventArgs args)
        {
            await SavePhotoAsync(args.ImageData);
        }

        private async Task SavePhotoAsync(byte[] photoData)
        {
            var readStatus = await CheckAndRequestPermissionAsync(new StorageRead());
            if (readStatus != PermissionStatus.Granted)
            {
                // Notify user permission was denied
                return;
            }

            var writeStatus = await CheckAndRequestPermissionAsync(new StorageWrite());
            if (writeStatus != PermissionStatus.Granted)
            {
                // Notify user permission was denied
                return;
            }

            var savedPhotoPath = await SavePhotoFileAsync(photoData, Guid.NewGuid().ToString() + ".jpg");

            LastSavedPhotoPath = savedPhotoPath;
        }

        private async Task<string> SavePhotoFileAsync(byte[] photoData, string fileName)
        {
            var dirPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
            Directory.CreateDirectory(dirPath);

            var savedPhotoPath = Path.Combine(dirPath, fileName);

            using (var fs = new FileStream(savedPhotoPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
            {
                await fs.WriteAsync(photoData, 0, photoData.Length);
                return savedPhotoPath;
            }
        }

        public async Task<PermissionStatus> CheckAndRequestPermissionAsync<T>(T permission) where T : BasePermission
        {
            var status = await permission.CheckStatusAsync();
            if (status != PermissionStatus.Granted)
            {
                status = await permission.RequestAsync();
            }

            return status;
        }
    }
}
  1. Launch app on an iOS device using Hot Restart and click the ‘Take photo’ button

Expected Behavior

CameraView takes a photo and triggers the MediaCaptured event

Actual Behavior

The MediaCaptured event doesn’t get triggered, the camera preview keeps going.

Basic Information

  • Version with issue: 1.0.2
  • Last known good version: ?
  • IDE: VS 2019 Professional v16.8.5
  • Platform Target Frameworks:
    • iOS: Default (14.3 I guess)
    • Android: 10.0
    • UWP: nope
  • Android Support Library Version: Android X
  • Nuget Packages:
    • Xamarin.Forms 5.0.0.1931
    • Xamarin.Essentials 1.6.1
  • Affected Devices:
    • iPad Air 2 (iOS 13.7)

Reproduction repo

https://github.com/michaldivis/camera-view-app

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 33 (10 by maintainers)

Most upvoted comments

This happens in production, App Store apps, not just in development. They get one or two photos and then it stops firing the event.

Still happening for me, even tried 1.3.0-pre1 and it still happening. Sometimes it works for one or two photos and then it stops working.

I didn’t get it to work in any version, at least not when using Hot Restart.

I never tried it when connected to a Mac (my setup doesn’t allow that), so I have no idea whether that works or not. And sadly, the iOS simulator doesn’t support a camera, so that’s not an option.

@ShinichiHezemansCortegs I’m not aware of any alternative to the CameraView. I’ve found many example implementations of a camera view renderer but none of them worked on all platforms, some didn’t work at all.

I’m going to assume this is fixed for now then. If that is not the case for Hot Restart @michaldivis please let me know!

Still happening for me. Sometimes it works for one or two photos and then it stops working. (1.2.0 version)

Resolved in the 1.2.0 version.

Hi - I’m having the same issue when calling cameraView.Shutter() the MediaCapture event isn’t being hit. I’m seeing this in Debug mode as well as Release mode. MediaCaptureFailed also doesn’t get hit

@pictos I’ve attached a reproduction repo. You can find it here: https://github.com/michaldivis/camera-view-app