Xamarin exception when framework callback passes an interface - xamarin

I'm trying to bind a framework written in Swift to my Xamarin application. The framework has a delegate with the following method:
onError(error: IError)
IError is a protocol in Swift (4):
#objc public protocol IError {
var message: String { get }
}
It's binding, as generated by sharpie and modified by me, looks like this:
// #protocol IError
[BaseType(typeof(NSObject))] // <- Added by me. Won't compile without it.
[Protocol, Model]
abstract class IError
{
// #required #property (readonly, copy, nonatomic) NSString * _Nonnull message;
[Abstract]
[Export("message")]
string Message { get; }
}
When the callback is being called, I get the following error in my application:
System.MemberAccessException has been thrown
Cannot create an instance of IError because it is an abstract class
Why is my application trying to instantiate IError? isn't the framework supposed to do this? Does it have to do with [BaseType(typeof(NSObject))]?

Finally found the solution here. You need to declare a bare interface with the same name but starting with 'I' in your binding and then it magically creates a wrapper for the interface which you receive instead of the real object.
The binding now looks like this:
//bare interface
interface IIError {}
// #protocol IError
[BaseType(typeof(NSObject))] // <- Added by me. Won't compile without
it.
[Protocol, Model]
abstract class IError
{
// #required #property (readonly, copy, nonatomic) NSString *
_Nonnull message;
[Abstract]
[Export("message")]
string Message { get; }
}
The binding for the method then looks like this:
[Abstract]
[Export("onError:")]
void OnError(IIError error);
EDIT:
It seems to also be documented here. Missed it somehow.

Related

Xamarin binding Events WeakDelegate issue

I have following
iOS code:
#protocol TestDelegate
- (void)onSuccess:(NSString*)token;
#end
#interface Utility : NSObject
#property (nullable, weak, getter = getTestDelegate, setter = setTestDelegate:) id<TestDelegate> delegate;
#end
and Sharpie generated code with delegate to event mapping:
[Protocol, Model]
public interface TestDelegate
{
[Export ("onSuccess:")]
void OnSuccess (string token);
}
[BaseType(typeof(NSObject),
Delegates = new string[] { "WeakDelegate" },
Events = new Type[] { typeof(TestDelegate) })
public interface Utility
{
[Wrap ("WeakDelegate")]
[NullAllowed]
TestDelegate Delegate { [Bind ("getTestDelegate")] get; [Bind ("setTestDelegate:")] set; }
[NullAllowed, Export ("delegate", ArgumentSemantic.Weak)]
NSObject WeakDelegate { [Bind ("getTestDelegate")] get; [Bind ("getTestDelegate:")] set; }
}
When I'm attaching only to the events and not using delegate property then events are not fired.
Correct me if I’m wrong but when using only events then nothing is referring to the internally created _XDelegate so it will be garbage collected right away.
In my case I had to store internal _XDelegate after attaching to the events to make them work.
public class TestClass
{
private readonly Delegate _del;
public TestClass()
{
iOS.SingletonInstance().OnSuccess += HandleOnSuccess;
_del = iOS.SingletonInstance().Delegate; //store Delegate instance to make events work
}
}
The issue was somewhere else - SingletonInstance() is a method instead of property so nothing is referring to the returned object so it's being GCed.

Xamarin iOS delegate BaseType usage

I'm just wondering why delegate in binding iOS project has to use BaseType(typeof(NSObject)) attribute when it's iOS counterpart does not use NSObject
iOS code:
#protocol TestDelegate
- (void)onSuccess:(NSString*)token;
#end
#interface Utility : NSObject
#property (nullable, weak, getter = getTestDelegate, setter = setTestDelegate:) id<TestDelegate> delegate;
#end
Sharpie code with delegate to event mapping added:
[Protocol, Model]
public interface TestDelegate
{
[Export ("onSuccess:")]
void OnSuccess (string token);
}
[BaseType(typeof(NSObject),
Delegates = new string[] { "WeakDelegate" },
Events = new Type[] { typeof(TestDelegate) })
public interface Utility
{
[Wrap ("WeakDelegate")]
[NullAllowed]
TestDelegate Delegate { [Bind ("getTestDelegate")] get; [Bind ("setTestDelegate:")] set; }
[NullAllowed, Export ("delegate", ArgumentSemantic.Weak)]
NSObject WeakDelegate { [Bind ("getTestDelegate")] get; [Bind ("getTestDelegate:")] set; }
}
BaseType attribute was not generated on TestDelegate by Sharpie because iOS native code was not using <NSObject> in its protocol.
This fails with "The type or namespace name TestDelegate' does not exist in the namespaceTest'. Are you missing an assembly reference? (CS0234) (Test.iOS)".
When I add [BaseType(typeof(NSObject))] on top of the TestDelegate it works like a charm.
The question is why this is needed?

(Xamarin IOS) What are the relationships between XXXDelegate class, IXXXDelegate interface and XXXDelegate_Extention staic class? When to use which?

I am a little confused about these types.
Especially XXXDelegate_Extention classes. If I want to implement an optional protocol method, will this XXXDelegate_Extention be useful? Or I always need to subclass the XXXDelegate class?
Thanks!
Delegates on iOS can have optional methods. Since the interface IXXDelegate only declares the non-optional functions, XXXDelegate only implements the non-optional functions. The XXXDelegate_Extention adds the optional functionsto the delegate (interface). So you can either inherit from XXXDelegate or implement IXXXDelegate completely on your own. I'd prefer inheriting, unless you really need to have a totally custom implementation.
Extension methods in C# are methods, that can be called like instance methods, but are not declared within the type of this instance.
Example on Extension methods
public class MyClass
{
public int Foo(int a)
{
return a + 1;
}
}
public static class MyClassExtension
{
public static int Bar(this MyClass my, int a)
{
return my.Foo(a+1);
}
}
var x = new MyClass();
x.Foo(2); // returns 3
x.Bar(2); // returns 4
More info: https://msdn.microsoft.com/en-us//library/bb383977.aspx

GetViewForAnnotation is never called

I am creating a MapView where I want to display some custom Annotations.
So I think usually what you do is add some IMKAnnotation to the MKMapView using the AddAnnotation method. I make sure to invoke that on the main thread like:
new NSObject().InvokeOnMainThread(() => {
_mapView.AddAnnotation(myNewAnnotation);
});
After these I are added I do see that the MKMapView now contains all the annotations I added in the Annotations property when inspecting with the debugger.
However, the problem is that GetViewForAnnotation is never invoked, no matter how I do it.
I've tried:
_mapView.GetViewForAnnotation += ViewForAnnotation;
private MKAnnotationView ViewForAnnotation(MKMapView mapView, IMKAnnotation annotation) {
// do stuff here
}
I've tried implementing my own delegate:
public class MyMapViewDelegate : MKMapViewDelegate
{
public override MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation) {
// do stuff
}
}
_delegate = new MyMapViewDelegate();
_mapView.Delegate = _delegate;
I've tried using WeakDelegate:
public class MapView : ViewController, IMKMapViewDelegate
{
private MKMapView _mapView;
public override void ViewDidLoad() {
_mapView = new MKMapView();
_mapView.WeakDelegate = this;
}
[Export("mapView:viewForAnnotation:")]
public MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation) {
// do stuff
}
}
Nothing seems to trigger the GetViewForAnnotation method. Any ideas what I do wrong?
EDIT:
A bit more details what I have now.
[Register("MapView")]
public class MapView : MvxViewController<MapViewModel>
{
private MKMapView _mapView;
private NMTAnnotationManager _annotationManager;
public override void ViewDidLoad()
{
base.ViewDidLoad();
_mapView = new MKMapView();
_mapView.GetViewForAnnotation += GetViewForAnnotation;
_annotationManager = new NMTAnnotationManager(_mapView);
var bindSet = this.CreateBindingSet<MapView, MapViewModel>();
bindSet.Bind(_annotationManager).For(a => a.ItemsSource).To(vm => vm.Locations).OneWay();
bindSet.Apply();
Add(_mapView);
View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
View.AddConstraints(
_mapView.AtTopOf(View),
_mapView.AtLeftOf(View),
_mapView.AtRightOf(View),
_mapView.AtBottomOf(View));
}
private MKAnnotationView GetViewForAnnotation(MKMapView mapview, IMKAnnotation annotation)
{
return null;
}
}
The NMTAnnotationManager simply weak subscribes to the INotifyCollectionChanged event the ObservableCollection which is used as ItemsSource in the binding. When the collection changes it simply adds and removes the Annotations from the MKMapView, nothing magical happens here. I have verified that it does indeed add, in this case 13, different IMKAnnotation instances to the MKMapView and they can be inspected in the Annotations property of it.
So as #Philip suggested in his answer, GetViewForAnnotation does get set before Annotations are added to the MapView. But if I put a breakpoint or some trace in the method it never gets hit.
The same code above, just with a simple MKMapViewDelegate like so:
public class MyMapViewDelegate : MKMapViewDelegate
{
public override void MapLoaded(MKMapView mapView)
{
Mvx.Trace("MapLoaded");
}
public override MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation)
{
Mvx.Trace("GetViewForAnnotation");
return null;
}
}
Does not work either. Although, the MapLoaded event gets hit for each time the map is rendered, but why does GetViewForAnnotation not get hit?
The way I'm doing it is:
Storyboard with a ViewController which contains a MKMapView
MKMapView Delegate to the ViewController is set in IB in the Connections Inspector
In ViewDidLoad I make sure I set this first:
mapView.GetViewForAnnotation += GetViewForAnnotation;
I had problems with it never beeing called too, and I fixed it with making sure that I set the event for GetViewForAnnotation before I added some annotations.
OK, so apparently the problem was PEBKAC.
The annotation indeed got added to the MKMapView. However, none of them had their Coordinate property set. So obviously the map did not know where to present them and never called GetViewForAnnotation.

Visual Studio code generated when choosing to explicitly implement interface

Sorry for the vague title, but I'm not sure what this is called.
Say I add IDisposable to my class, Visual Studio can create the method stub for me. But it creates the stub like:
void IDisposable.Dispose()
I don't follow what this syntax is doing. Why do it like this instead of public void Dispose()?
And with the first syntax, I couldn't work out how to call Dispose() from within my class (in my destructor).
When you implement an interface member explicitly, which is what the generated code is doing, you can't access the member through the class instance. Instead you have to call it through an instance of the interface. For example:
class MyClass : IDisposable
{
void IDisposable.Dispose()
{
// Do Stuff
}
~MyClass()
{
IDisposable me = (IDisposable)this;
me.Dispose();
}
}
This enables you to implement two interfaces with a member of the same name and explicitly call either member independently.
interface IExplict1
{
string InterfaceName();
}
interface IExplict2
{
string InterfaceName();
}
class MyClass : IExplict1, IExplict2
{
string IExplict1.InterfaceName()
{
return "IExplicit1";
}
string IExplict2.InterfaceName()
{
return "IExplicit2";
}
}
public static void Main()
{
MyClass myInstance = new MyClass();
Console.WriteLine( ((IExplcit1)myInstance).InstanceName() ); // outputs "IExplicit1"
IExplicit2 myExplicit2Instance = (IExplicit2)myInstance;
Console.WriteLine( myExplicit2Instance.InstanceName() ); // outputs "IExplicit2"
}
Visual studio gives you two options:
Implement
Implement explicit
You normally choose the first one (non-explicit): which gives you the behaviour you want.
The "explicit" option is useful if you inherit the same method from two different interfaces, i.e multiple inheritance (which isn't usually).
Members of an interface type are always public. Which requires their method implementation to be public as well. This doesn't compile for example:
interface IFoo { void Bar(); }
class Baz : IFoo {
private void Bar() { } // CS0737
}
Explicit interface implementation provides a syntax that allows the method to be private:
class Baz : IFoo {
void IFoo.Bar() { } // No error
}
A classic use for this is to hide the implementation of a base interface type. IEnumerable<> would be a very good example:
class Baz : IEnumerable<Foo> {
public IEnumerator<Foo> GetEnumerator() {}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { }
}
Note how the generic version is accessible, the non-generic version is hidden. That both discourages its use and avoids a compile error because of a duplicate method.
In your case, implementing Dispose() explicitly is wrong. You wrote Dispose() to allow the client code to call it, forcing it to cast to IDisposable to make the call doesn't make sense.
Also, calling Dispose() from a finalizer is a code smell. The standard pattern is to add a protected Dispose(bool disposing) method to your class.

Resources