2
votes

When binding a iOS lib.a for Xamarin, I'm getting the following error:

btouch: The delegate method Device.SomeDeviceDelegate.CaptureComplete needs to take at least one parameter (BI1003)

The bindings were generated Objective Sharpie.

namespace Device 
{   

    // @protocol SomeDeviceDelegate <NSObject>
    [Protocol, Model, Preserve]
    [BaseType(typeof(NSObject))]
    interface SomeDeviceDelegate
    {
        // @optional -(void)CaptureComplete;
        [Export("CaptureComplete")]
        void CaptureComplete();
    }

    // @interface SomeDevice : NSObject
    [Protocol, Model, Preserve]
    [BaseType(typeof(NSObject), Delegates = new[] { "WeakDelegate" }, Events = new[] { typeof(SomeDeviceDelegate) })]
    interface SomeDevice
    {
        [Wrap("WeakDelegate")]
        SomeDeviceDelegate Delegate { get; set; }

        // @property (assign, nonatomic) id<SomeDeviceDelegate> delegate;
        [NullAllowed, Export("delegate", ArgumentSemantic.Assign)]
        SomeDeviceDelegate WeakDelegate { get; set; }
    }

}

NB. I've changed the name to SomeDevice to hide the hardware/device name (NDA).

The compiler is complaining that the // @optional -(void)CaptureComplete; and respective binding CaptureComplete() has no parameters and that it needs at least one.

Q: What do I need to do to bind this delegate?

I have tried the Binding Types Reference Guide and have tried applying the

  • EventArgs attribute
  • NoDefaultValue attribute
  • DefaultValueFromArgument attribute

UPDATE

I misunderstood the NoDefaultValue and DefaultValueFromArgument attributes, they are used when the delegate returns a value (e.g. bool) as the return interferes with the Xamarin wrapping of the event.

2

2 Answers

2
votes

I found the solution late yesterday and closing out this question for others who encounter the same error message.

NB: Haven't change the names this time as it made the answer less clear.

The preferred way of handling ObjC delegate is to expose them as Events, e.g.

// @interface ICBarCodeReader : ICISMPDevice
[DisableDefaultCtor]
[BaseType(typeof(ICISMPDevice), Delegates = new[] { "WeakDelegate" }, Events = new[] { typeof(ICBarCodeReaderDelegate) }))]
public interface ICBarCodeReader
{
    [Wrap("WeakDelegate")]
    ICBarCodeReaderDelegate Delegate { get; set; }

    // @property (assign, nonatomic) id<ICISMPDeviceDelegate,ICBarCodeReaderDelegate> delegate;
    [NullAllowed, Export("delegate", ArgumentSemantic.Assign)]
    ICBarCodeReaderDelegate WeakDelegate { get; set; }
}

The Delegate and Events parameter of BaseType, generates codes that wraps each method on the ICBarCodeReaderDelegate.

// @protocol ICBarCodeReaderDelegate
[Protocol, Model, Preserve]
[BaseType(typeof(ICISMPDeviceDelegate))]
public interface ICBarCodeReaderDelegate
{
    // @required -(void)barcodeData:(id)data ofType:(int)type;
    [Abstract]
    [Export("barcodeData:ofType:")]
    [EventArgs("BarcodeData")]
    void BarcodeData(string data, BarCodeSymbologies type);

    // @required -(void)configurationRequest;
    [Abstract]
    [Export("configurationRequest")]
    void ConfigurationRequest();
}

Which allows you in your project to do:

public void Init()
{
    _sharedBarCodeReader.BarcodeData += OnBarcodeData;
}

private void OnBarcodeData(object sender, BarcodeDataEventArgs e)
{
    var barcode = Convert.ToString(sender); // this maps to string data
    //BarCodeSymbologies is in BarcodeDataEventArgs

    var handler = BarCodeData;
    if (handler != null)
        handler(this, barcode);
}

However this approach fails when the method has no parameters, as told by the btouch error message.

The other approach that I only discovered (and now appears straight forward and simple) is not to wrap the delegate as events, e.g.

// @interface ICBarCodeReader : ICISMPDevice
[DisableDefaultCtor]
[BaseType(typeof(ICISMPDevice))]
public interface ICBarCodeReader
{
    [Wrap("WeakDelegate")]
    ICBarCodeReaderDelegate Delegate { get; set; }

    // @property (assign, nonatomic) id<ICISMPDeviceDelegate,ICBarCodeReaderDelegate> delegate;
    [NullAllowed, Export("delegate", ArgumentSemantic.Assign)]
    ICBarCodeReaderDelegate WeakDelegate { get; set; }
}

And instead create a implementation of the Delegate interface.

e.g.

public void Init()
{
    _sharedBarCodeReader.Delegate = new BarCodeReaderDelegate(this);
}

private class BarCodeReaderDelegate : ICBarCodeReaderDelegate
{
    public BarCodeReaderDelegate(BarCodeScanner barCodeScanner)
    {
        _barCodeScanner = barCodeScanner;
    }

    public override void BarcodeData(string data, BarCodeSymbologies type)
    {
        var handler = _barCodeScanner.BarCodeData;
        if (handler != null)
            handler(this, data);
    }

    public override void ConfigurationRequest() { }

    private readonly BarCodeScanner _barCodeScanner;
}

I hope this helps someone else.

0
votes

"CaptureComplete:" might be expecting a sender?