Reputation: 20561
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
attributeNoDefaultValue
attributeDefaultValueFromArgument
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.
Upvotes: 2
Views: 1037
Reputation: 20561
I found a solution.
NB: I 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;
}
Upvotes: 2