Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IOS App crash with Null Reference Exception at SetupCaptureSession() #1078

Open
jamesingreersc opened this issue Jun 23, 2023 · 6 comments
Open

Comments

@jamesingreersc
Copy link

I am in the process of updating a native xamarin ios app to a Microsoft.IOS .Net 6 app. I am using the ZXing.Net6.Mobile nuget which I used to replace the older ZXing.Net.Mobile .

The code being used was seamless as far as building but I am getting a null reference exception when calling the .Scan method. It's crashing all the way back to the Main application file. Just before the crash the camera does try to open.

This is the stack trace I am getting
object reference not set to instance of an object
at ZXing.Mobile.ZXingScannerView.SetupCaptureSession()
at ZXing.Mobile.ZXingScannerView.b__42_0()
at Foundation.NSActionDispatcher.Apply()
at Foundation.NSObject.InvokeOnMainThread(Action )
at ZXing.Mobile.ZXingScannerView.StartScanning(Action`1 scanResultHandler, MobileBarcodeScanningOptions options)
at ZXing.Mobile.ZXingScannerViewController.b__33_1()
at Foundation.NSAsyncActionDispatcher.Apply()
at UIKit.UIApplication.UIApplicationMain(Int32 , String[] , IntPtr , IntPtr )
at UIKit.UIApplication.Main(String[] , Type , Type )
at DSDRouteManager.iOS.Application.Main(String[] args) in /Users/jamespridgen/Desktop/DSDRouteManager/DSDRouteManager.IOS/Main.cs:line

These are the keys I have in Info.plist
NSCameraUsageDescription
Can we use your camera
Privacy - Camera Usage Description
Taking picture barcode
Privacy - Photo Library Usage Description
Taking picture barcode

This is the code I am using to call the camera

          var auth = AVCaptureDevice.GetAuthorizationStatus(AVAuthorizationMediaType.Video);
          if (auth == AVAuthorizationStatus.NotDetermined)
          {
              if (AVCaptureDevice.RequestAccessForMediaTypeAsync(AVAuthorizationMediaType.Video).Result)
              {
                  auth = AVCaptureDevice.GetAuthorizationStatus(AVAuthorizationMediaType.Video);
              }
          }

       // camera access has been granted at this point
          var cameraScanner = new MobileBarcodeScanner(this); // "this" is the UIViewController
          var options = Util.GetMobileBarcodeScanningOptions();
          var result = await cameraScanner.Scan(options,true); // crash happens here

          
          Scanning options
          
 return new MobileBarcodeScanningOptions()
   {
       AutoRotate = true,
       PossibleFormats = new List<BarcodeFormat>()
       {
           BarcodeFormat.All_1D,
           BarcodeFormat.QR_CODE,
           BarcodeFormat.CODE_39,
           BarcodeFormat.CODE_128,
           BarcodeFormat.UPC_A,
           BarcodeFormat.UPC_E
       }
   };
   
   Do I need some kind of initialization that the older nuget didn't require.
   
   Any help would be much appreciated
   James
@spinspin
Copy link

@jamesingreersc Did you find a solution to this? I'm running into the same issue .

@jamesingreersc
Copy link
Author

@spinspin No, I found nothing helpful. I can see that the UINavigationController is null when the scanner is being initialized. I'm not sure if this a bug in .Net or not. I am going to test the old version of the app that does work and see if it's null there or not.

@spinspin
Copy link

spinspin commented Jun 30, 2023

It's probably a bug. I've just written some native net7.0-ios code which handles barcode scanning so I don't need to use this library let me know if you want it.

@jamesingreersc
Copy link
Author

@spinspin That would be awesome if I could use your code. Thanks bunches!!

@spinspin
Copy link

spinspin commented Jul 2, 2023

@jamesingreersc this should be enough to get going , I'm using MvvmCross but the core functionality to create a barcode scanner is in here

public class BarcodeScannerViewController : MvxViewController
{
AVCaptureSession captureSession;
AVCaptureVideoPreviewLayer previewLayer;
public Action BarcodeScanned { get; set; }

    public BarcodeScannerViewController(IntPtr handle) : base(handle) { }

    public BarcodeScannerViewController(){}

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        SetUI();
        StartScanning();
    }

    private void SetUI()
    {        
        View.BackgroundColor = UIColor.White;

        NavigationController.NavigationBar.TitleTextAttributes = new UIStringAttributes
        {
            Font = UIFont.FromName("Gill Sans", 20),
            ForegroundColor = UIColor.Black
        };

        NavigationItem.BackButtonTitle = "";
        NavigationItem.Title = "Barcode Scanning";

        var closeImage = UIImage.FromBundle("Close36pt").ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate);

        if (DeviceInfo.Version.Major >= 13)
        {
            closeImage.ApplyTintColor(UIColor.Black);
        }

        var closeButton = new UIButton();
        closeButton.SetImage(closeImage, UIControlState.Normal);
        closeButton.TintColor = UIColor.Black;
        NavigationItem.SetRightBarButtonItem(new UIBarButtonItem(closeButton),true);

        closeButton.TouchUpInside += (sender, args) =>
        {
            ViewModel.OnBarcodeScanCancel();
        };
    }


    void StartScanning()
    {
        try
        {
            captureSession = new AVCaptureSession();
            var videoCaptureDevice = AVCaptureDevice.GetDefaultDevice(AVMediaTypes.Video);
            // Create an input from the video capture device
            var videoInput = AVCaptureDeviceInput.FromDevice(videoCaptureDevice, out NSError error);

            if (error != null)
            {
                ViewModel.OnBarcodeScanError();
                return;
            }
            else
            {
                captureSession.AddInput(videoInput);
            }

            // Create a metadata output and set its delegate
            var metadataOutput = new AVCaptureMetadataOutput
            {
                RectOfInterest = UIScreen.MainScreen.Bounds
            };

            metadataOutput.SetDelegate(new OutputDelegate(DidScanBarcode), DispatchQueue.MainQueue);
            captureSession.AddOutput(metadataOutput);
            metadataOutput.MetadataObjectTypes = AVMetadataObjectType.EAN13Code
                                   | AVMetadataObjectType.QRCode
                                   | AVMetadataObjectType.EAN8Code
                                   | AVMetadataObjectType.Code128Code;

            // Create a preview layer to show the view from the camera
            previewLayer = new AVCaptureVideoPreviewLayer(captureSession)
            {
                Frame = new CGRect(View.Bounds.X, View.Bounds.Y +50 , View.Bounds.Width, View.Bounds.Height-50)
            };


            View.Layer.AddSublayer(previewLayer);

            // Create a new UIView with the desired dimensions.
            var redLineView = new UIView
            {
                BackgroundColor = UIColor.Red,
                Frame = new CGRect(0, previewLayer.Frame.GetMidY(), View.Bounds.Width, 2) // This line is 2 points thick and centered in the preview layer.
            };

            // Add the view to the parent view.
            View.AddSubview(redLineView);

            // Start the capture session
            captureSession.StartRunning();
        }

        catch (Exception)
        {
            ViewModel.OnBarcodeScanError();
        }
    }

    private void DidScanBarcode(string barcode)
    {
        // Stop the capture session and notify the delegate
        captureSession.StopRunning();
        captureSession.Dispose(); // release the capture session
        captureSession = null; // set it to null

        // Remove the preview layer
        previewLayer.RemoveFromSuperLayer();
        previewLayer.Dispose(); // Dispose the preview layer
        previewLayer = null; // set it to null

        ViewModel.OnBarcodeScanSuccess(barcode);
    }


    public override void ViewWillDisappear(bool animated)
    {
        base.ViewWillDisappear(animated);

        if (IsBeingDismissed || IsMovingFromParentViewController)
        {
            Cleanup();
        }
    }

    void Cleanup()
    {
        // Stop the capture session and release resources
        captureSession.StopRunning();
        captureSession.Dispose();
        captureSession = null;

        previewLayer.RemoveFromSuperLayer();
        previewLayer.Dispose();
        previewLayer = null;
    }
}

class OutputDelegate : AVCaptureMetadataOutputObjectsDelegate
{
    Action<string> barcodeHandler;

    public OutputDelegate(Action<string> barcodeHandler)
    {
        this.barcodeHandler = barcodeHandler;
    }

    public override void DidOutputMetadataObjects(AVCaptureMetadataOutput captureOutput, AVMetadataObject[] metadataObjects, AVCaptureConnection connection)
    {
        if (metadataObjects != null && metadataObjects.Length > 0)
        {
            var readableObject = metadataObjects[0] as AVMetadataMachineReadableCodeObject;

            if (readableObject != null)
            {
                var barcode = readableObject.StringValue;
                barcodeHandler?.Invoke(barcode);
            }
        }
    }
}

public interface IBarcodeScanningServiceDelegate
{
    void DidFinishScanning(string barcode);
}

@jamesingreersc
Copy link
Author

@spinspin Thanks for this. It helped a bunch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants