Binding a delegate that has no parameters, btouch compile error

DennisRocheDennisRoche USMember, University
edited April 2015 in Xamarin.iOS

I'm trying to bind a native library that integrates to a hardware device (Ingenico iSMP POS).

I used Objective Sharpie to generate the follow bindings* from their .h file.

[Protocol, Model, Preserve]
[BaseType(typeof(ICISMPDeviceDelegate))]
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();
}

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

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

* minimium example for purpose of this question

When compiling this in Xamarin Studio, I get the following error message

btouch: The delegate method ICBarCodeReaderDelegate.ConfigurationRequest needs to take at least one parameter (BI1003)

As I understand it, btouch needs ConfigurationRequest to have at least one paramater (i.e. a sender) to generate the event delegate. However the binding generated from the header // @required -(void)configurationRequest; doesn't specify any parameters.

I have tried the the following:

  • adding a : to the export; and
  • adding a sender parameter

e.g.

[Protocol, Model, Preserve]
[BaseType(typeof(ICISMPDeviceDelegate))]
interface ICBarCodeReaderDelegate
{
    // @required -(void)configurationRequest;
    [Abstract]
    [Export("configurationRequest:")]
    void ConfigurationRequest(ICBarCodeReader sender);
}

This compiles and links, however fails at runtime with: ObjCRuntime.RuntimeException: Wrapper type 'ICBarCodeReaderDelegate' is missing its native ObjectiveC class 'ICBarCodeReaderDelegate'., which I assume is because that method/export signature does not exist.

Does anyone know how to bind this delegate?

Tagged:

Best Answer

Answers

  • DennisRocheDennisRoche USMember, University

    Answering my own question.

    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.

  • MarkRadacz.7953MarkRadacz.7953 USInsider, University, Developer Group Leader ✭✭

    @Denis well done, about to start working on bindings for Ingenico, could you share the link to your lib?

  • DennisRocheDennisRoche USMember, University

    @MarkRadacz.1850 sorry, I'm unable to share the library as it was built for a client.

    Following the above example, you will be able to create the bindings easily enough. Follow the Objective Sharpie tutorial and bind against the frameworks. You will have to manually add frameworks. Check lipo --info *.a to find out the platforms to support.

Sign In or Register to comment.