Xamarin iOS binding library duplicated constructors issue - xamarin

I have an issue with duplicated constructor in Xamarin iOS binding library with code generated by sharpie tool from third-party SDK code. Basicly C# generated interface is using NSFileHandle as a base type and SDK header file declares identical designated initializer in its subclass like in NSFileHandler so I'm getting "Member ... is already defined error" because now binding library is generating C# constructor twice - first time from the base class and the second from subclassed initializer.
Objective-C code:
#interface MyFileHandle : NSFileHandle
//...
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
//...
C# binding library code:
[BaseType(typeof(NSFileHandle))]
public interface MyFileHandle
{
//...
[Export("initWithCoder:")]
[DesignatedInitializer]
IntPtr Constructor(NSCoder coder);
//...
}
Binding library generated code (*.g.cs):
[Register("MyFileHandle", true)]
public unsafe partial class MyFileHandle : NSFileHandle {
//...
[CompilerGenerated]
[DesignatedInitializer]
[EditorBrowsable (EditorBrowsableState.Advanced)]
[Export ("initWithCoder:")]
public MyFileHandle (NSCoder coder) : base (NSObjectFlag.Empty)
{
//...
}
[Export ("initWithCoder:")]
[DesignatedInitializer]
[CompilerGenerated]
public MyFileHandle (NSCoder coder)
: base (NSObjectFlag.Empty)
{
//...
}
//...
}
How can I prevent binding library from generating constructors twice thus get rid of the error?

It seems that you can simply remove duplicated Constructor from ApiDefinitions.cs as #SushiHangover suggested.

Related

Xamarin iOS Google Sign-in component : App crashes due to error uiDelegate must either be a |UIViewController| or implement

I'm trying to implement google sign in using this component for xamarin.ios: Google Sign-in for iOS
It works great on emulator but when it comes to actual device it's crashing once i tap signin button. (iOS 10.2 - emulator is also using same OS)
I have a custom button which calls SignInUser method on SignIn.SharedInstance
It's crashing with below error (only when the app is deployed on device)
Objective-C exception thrown. Name: NSInvalidArgumentException Reason: uiDelegate must either be a |UIViewController| or implement the |signIn:presentViewController:| and |signIn:dismissViewController:| methods from |GIDSignInUIDelegate|.
I'm calling function below to initialize GoogleSignIn on FinishedLaunching method of AppDelegate.cs
public void Configure()
{
NSError configureError;
Context.SharedInstance.Configure(out configureError);
if (configureError != null)
{
// If something went wrong, assign the clientID manually
Console.WriteLine("Error configuring the Google context: {0}", configureError);
SignIn.SharedInstance.ClientID = googleClientId;
}
SignIn.SharedInstance.Delegate = this;
SignIn.SharedInstance.UIDelegate = new GoogleSignInUIDelegate();
}
Here's my implementation of ISignInUIDelegate():
class GoogleSignInUIDelegate : SignInUIDelegate
{
public override void WillDispatch(SignIn signIn, NSError error)
{
}
public override void PresentViewController(SignIn signIn, UIViewController viewController)
{
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(viewController, true, null);
}
public override void DismissViewController(SignIn signIn, UIViewController viewController)
{
UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);
}
}
So the emulator seems to know the methods are implemented, but not the device. Any idea what i am doing wrong here?
After some debugging i found where the actual issue was.
Somehow, the UIDelegate i assigned during initialization was lost when i was calling my login method. So i moved the line below from my initialization step to login
SignIn.SharedInstance.UIDelegate = new GoogleSignInUIDelegate();
Here's how my login method looks like now:
public void Login()
{
SignIn.SharedInstance.UIDelegate = new GoogleSignInUIDelegate(); //moved this here from Configure
SignIn.SharedInstance.SignInUser();
}
This took care of the issue for me but i am still not sure why this is only an issue on the device and not the emulator. Any Ideas?
Add a PreserveAttribute to your GoogleSignInUIDelegate class to prevent the Linker from removing the methods that can not be determined via static analysis.
Add the following class to your project:
public sealed class PreserveAttribute : System.Attribute {
public bool AllMembers;
public bool Conditional;
}
Apply the class attribute:
[Preserve (AllMembers = true)]
class GoogleSignInUIDelegate : SignInUIDelegate
{
~~~~
}
Re: https://developer.xamarin.com/guides/ios/advanced_topics/linker/
Setting PresentingViewController helped me to resolve the issue.
SignIn.SharedInstance.PresentingViewController = this;
Have found such fix here:
https://github.com/googlesamples/google-signin-unity/issues/169#issuecomment-791305225

Xamarin Objective Sharpie Binding Delegate to Interface

I’m trying to bind an objective-c library with a delegate
#protocol PKTokenFieldDelegate <UITextFieldDelegate>
-(void)tokenShouldChangeHeight:(CGFloat)height;
#optional
-(void)tokenFieldDidSelectToken:(PKToken*)token;
-(void)tokenFieldDidBeginEditing:(PKTokenField*)tokenField;
-(void)tokenFieldDidEndEditing:(PKTokenField*)tokenField;
#end
Sharpie output based on the walkthrough on xamarin developer site.
// #protocol PKTokenFieldDelegate <UITextFieldDelegate>
[BaseType (typeof (NSObject))]
[Model]
interface PKTokenFieldDelegate : IUITextFieldDelegate
{
// #required -(void)tokenShouldChangeHeight:(CGFloat)height;
[Abstract]
[Export ("tokenShouldChangeHeight:")]
void TokenShouldChangeHeight (nfloat height);
// #optional -(void)tokenFieldDidSelectToken:(PKToken *)token;
[Export ("tokenFieldDidSelectToken:")]
void TokenFieldDidSelectToken (PKToken token);
// #optional -(void)tokenFieldDidBeginEditing:(PKTokenField *)tokenField;
[Export ("tokenFieldDidBeginEditing:")]
void TokenFieldDidBeginEditing (PKTokenField tokenField);
// #optional -(void)tokenFieldDidEndEditing:(PKTokenField *)tokenField;
[Export ("tokenFieldDidEndEditing:")]
void TokenFieldDidEndEditing (PKTokenField tokenField);
}
This only creates an object that I can inherit from instead of creating an interface. I need to have this as an interface. What am I missing?
Thanks
I just had to change the [Model] to [Protocol] in order for this to work.
I also ran into a problem when the namespace and class name are the same that you get errors. Which is what got me down the road of changing Protocol to Model in the first place.

A delegate to a virtual method where does it point to (base/derived)?

I recently started using C++/Cli for wrapping purposes.
Now I'm at a point where I've to know more about the internals.
Consider the following code:
Header file (ignoring .NET namespaces for this example):
public ref class BaseyClass
{
protected:
delegate void TestMethodDelegate(); // TestMethod delegate
BaseyClass(); // constructor
virtual void TestMethod(); // member: method
GCHandle _testMethodHandle; // member: method handle
};
CPP file (ignoring .NET namespaces for this example):
BaseyClass::BaseyClass()
{
_testMethodHandle
= GCHandle::Alloc(
gcnew TestMethodDelegate(this, &BaseyClass::TestMethod));
}
void TestMethod()
{
}
Eventually this class will be used as base class (for a DerivedClass) later and the method "TestMethod()" gets overridden and called from unmanaged code through the delegate pointer.
Now the question: Which method will be referenced by the delegate?
BaseyClass::TestMethod();
or
DerivedClass::TestMethod();
Personally I think the "BaseyClass::TestMethod()" will be referenced by the delegate because even when it's overridden, the delegate points to the (base-)address of BaseyClass. Hence a DerivedClass cannot override the "TestMethod" and use the delegate from BaseyClass.
I just want to be sure. Thanks for your comments and enlightment.
The delegate will be a reference to the derived class's TestMethod. Even though you're passing &BaseyClass::TestMethod, that's a virtual method, you're also passing this, which is the derived type, and both of those are taken into account when the delegate is created.
Other notes:
TestMethodDelegate doesn't need to be inside the class definition. The more standard way is to have the delegate outside of the class, just in the namespace. (Or use the existing built-in one, Action.)
You don't need to GCHandle::Alloc (I assume that's what you meant by Allow). Instead, declare _testMethodHandle as TestMethodDelegate^ (or Action^). In general, you shouldn't need to deal with GCHandle unless you're interfacing with unmanaged code, and this code is all managed.
Here's my test code:
public ref class BaseyClass
{
public:
BaseyClass() { this->_testMethodHandle = gcnew Action(this, &BaseyClass::TestMethod); }
virtual void TestMethod() { Debug::WriteLine("BaseyClass::TestMethod"); }
Action^ _testMethodHandle;
};
public ref class DerivedClass : BaseyClass
{
public:
virtual void TestMethod() override { Debug::WriteLine("DerivedClass::TestMethod"); }
};
int main(array<System::String ^> ^args)
{
BaseyClass^ base = gcnew DerivedClass();
base->_testMethodHandle();
return 0;
}
Output:
DerivedClass::TestMethod

Obj-C Private Method Compiler Warnings

Private methods are a useful construct to keep code organised within class boundaries. An example being the organisation of long winded Quartz 2d instructions in a custom UIView subclass. I am able to include such methods in '.m' files with no declaration in '.h'. A working example from a UIView subclass '.m' file reads:
-(void)DoSomethingPrivate { //Not declared in interface
NSLog(#"Does this print a private function?");
}
- (id)initWithFrame:(CGRect)frame //Declared in inherited interface
{
self = [super initWithFrame:frame];
if (self) {
[self DoSomethingPrivate]; //Error: 'Instance method not found'
} //... but it works anyway.
return self;
}
My problem is that the compiler generates the warning "Instance method '-DoSomethingPrivate' not found (return type defaults to 'id')" on the line calling the private function. I'm aware from responses to this question that I can use a 'no name' interface category to 'hide' private method declarations.
However, when I review Apple sample code SimpleStocks, file "SimpleStockView.m", it contains a private function which is neither declared in a no-name category interface, nor does it generate compiler warnings:
//Private Function
- (void)drawRadialGradientInSize:(CGSize)size centeredAt:(CGPoint)center {
...
}
//Is called by this function...
- (UIImage *)patternImageOfSize:(CGSize)size {
...
//The next line doesn't generate any warnings!
[self drawRadialGradientInSize:size centeredAt:center];
...
}
I'd be grateful if anyone can shed any light on how Apple's sample-code private-methods appear to escape compiler checks, so I may avoid having to maintain a 'no-name' category header with all my private methods.
Many thanks.

How to declare a function in Cocoa after the function using it?

I'm slowly building my application to a working state.
I'm using two functions called setCollection and addToCollection. These functions both accept NSArray as input.
I also have a function called add in which I use both of these functions. When I try to compile, Xcode shows an error:
'setCollection' undeclared (first use in this function)
I guess this has to do with the function called being defined below the active function. Another guess would be that the functions should be globalized to be useable inside my add function.
I'm normally a php coder. the way Php handles this is the first one. The functions called should be before the functions using them, because otherwise they just don't exist. Is there a way to make functions still to come available at runtime, or should I rearrange all functions to make them function properly?
You can declare functions ahead of time as follows:
void setCollection(NSArray * array);
void addToCollection(NSArray * array);
//...
// code that calls setCollection or addToCollection
//...
void setCollection(NSArray * array)
{
// your code here
}
void addToCollection(NSArray * array)
{
// your code here
}
If you are creating a custom class, and these are member functions (usually called methods in Objective-C) then you would declare the methods in your class header and define them in your class source file:
//MyClass.h:
#interface MyClass : NSObject
{
}
- (void)setCollection:(NSArray *)array;
- (void)addToCollection:(NSArray *)array;
#end
//MyClass.m:
#import "MyClass.h"
#implementation MyClass
- (void)setCollection:(NSArray *)array
{
// your code here
}
- (void)addToCollection:(NSArray *)array
{
// your code here
}
#end
//some other source file
#import "MyClass.h"
//...
MyClass * collection = [[MyClass alloc] init];
[collection setCollection:someArray];
[collection addToCollection:someArray];
//...
If your functions are global (not part of a class), you just have to put the declaration before the use, just like eJames suggests.
If your functions actually are methods (part of a class), you have to declare an anonymous category of your class before the implementation and put your method declarations in this interface:
#interface Myclass()
- (void) setCollection:(NSArray*)array;
- (void) addToCollection:(NSArray*)array;
#end
#implementation Myclass
// Code that calls setCollection or addToCollection
- (void) setCollection:(NSArray*)array
{
// your code here
}
- (void) addToCollection:(NSArray*)array
{
// your code here
}
#end
This way, you don't need to expose your functions in the main interface of MyClass.

Resources