2

I am struggling to get my first Xamarin binding to work. I am trying to create a binding of the FlatUIKit library : https://github.com/Grouper/FlatUIKit.

I managed to create the fat static library (universal .a) of the above project. I used Objective Sharpie to generate the ApiDefinition.cs for me. I created a BindingProject in Xamarin using that ApiDefinition.cs (slightly modified to make the project compile). I took that new .dll and tried it in a new project. Calling methods from that .dll works fine and compiles. However, I get issues at runtime. All the static calls done to a FlatUI_UIColor return null. This makes this API unusable.

Here is the initial .h that contains the colors:

@interface UIColor (FlatUI)

  • (UIColor *) colorFromHexCode:(NSString *)hexString;
  • (UIColor *) turquoiseColor;
  • (UIColor *) greenSeaColor;
  • (UIColor *) emerlandColor;
  • (UIColor *) nephritisColor;
  • (UIColor *) peterRiverColor;
  • (UIColor *) belizeHoleColor;
  • (UIColor *) amethystColor;
  • (UIColor *) wisteriaColor;
  • (UIColor *) wetAsphaltColor;
  • (UIColor *) midnightBlueColor;
  • (UIColor *) sunflowerColor;
  • (UIColor *) tangerineColor;
  • (UIColor *) carrotColor;
  • (UIColor *) pumpkinColor;
  • (UIColor *) alizarinColor;
  • (UIColor *) pomegranateColor;
  • (UIColor *) cloudsColor;
  • (UIColor *) silverColor;
  • (UIColor *) concreteColor;
  • (UIColor *) asbestosColor;

  • (UIColor *) blendedColorWithForegroundColor:(UIColor *)foregroundColor backgroundColor:(UIColor *)backgroundColor percentBlend:(CGFloat) percentBlend;

@end

Below is the binding produced by Objective Sharpie:

[Category, BaseType (typeof (UIColor))]
public partial interface FlatUI_UIColor {

    [Static, Export ("colorFromHexCode:")]
    UIColor ColorFromHexCode (string hexString);

    [Static, Export ("turquoiseColor"), Verify ("ObjC method massaged into getter property", "/Users/jhondel/Projects/iOS Libraries/FlatUIKit/Classes/ios/UIColor+FlatUI.h", Line = 14)]
    UIColor TurquoiseColor { get; }

    [Static, Export ("greenSeaColor"), Verify ("ObjC method massaged into getter property", "/Users/jhondel/Projects/iOS Libraries/FlatUIKit/Classes/ios/UIColor+FlatUI.h", Line = 15)]
    UIColor GreenSeaColor { get; }

    /// ...

    [Static, Export ("blendedColorWithForegroundColor:backgroundColor:percentBlend:")]
    UIColor BlendedColorWithForegroundColor (UIColor foregroundColor, UIColor backgroundColor, float percentBlend);
}

In ApiDefintion.cs, I get rid of the verify.

If it helps, this is the .m of the initial library:

@implementation UIColor (FlatUI)

// Thanks to http://stackoverflow.com/questions/3805177/how-to-convert-hex-rgb-color-codes-to-uicolor
+ (UIColor *) colorFromHexCode:(NSString *)hexString {
    NSString *cleanString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""];
    if ([cleanString length] == 3) {
        cleanString = [NSString stringWithFormat:@"%@%@%@%@%@%@",
                       [cleanString substringWithRange:NSMakeRange(0, 1)],[cleanString substringWithRange:NSMakeRange(0, 1)],
                       [cleanString substringWithRange:NSMakeRange(1, 1)],[cleanString substringWithRange:NSMakeRange(1, 1)],
                       [cleanString substringWithRange:NSMakeRange(2, 1)],[cleanString substringWithRange:NSMakeRange(2, 1)]];
    }
    if([cleanString length] == 6) {
        cleanString = [cleanString stringByAppendingString:@"ff"];
    }

    unsigned int baseValue;
    [[NSScanner scannerWithString:cleanString] scanHexInt:&baseValue];

    float red = ((baseValue >> 24) & 0xFF)/255.0f;
    float green = ((baseValue >> 16) & 0xFF)/255.0f;
    float blue = ((baseValue >> 8) & 0xFF)/255.0f;
    float alpha = ((baseValue >> 0) & 0xFF)/255.0f;

    return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
}

+ (UIColor *) turquoiseColor {
    static UIColor *turquoise = nil;
    static dispatch_once_t dispatchToken;

    dispatch_once(&dispatchToken, ^{
        turquoise = [UIColor colorFromHexCode:@"1ABC9C"];
    });

    return turquoise;
}

Any ideas why FlatUI_UIColor.GreenSeaColor (and the others) return null?

Thanks for your help.

UPDATE after @jstedfast comment:

Thanks for your reply.

I have built the fat library using the makefile in https://github.com/xamarin/monotouch-samples/blob/master/BindingSample/src/binding/Makefile.

It builds a Universal library (which gathers armv7 and i386).
When I import this .a in the binding project in Xamarin, it automatically generates the LinkWith. In my case:

[assembly: LinkWith ("libXMBindingLibrarySampleUniversal.a", LinkTarget.ArmV7 | LinkTarget.Simulator, ForceLoad = true)]  

The library only uses "UIKit" and "Foundation" which, according to the docs, do not need to be specified in the LinkWith.

The generated .dll seems fine. I have access to its methods in my example project. I manage to create a FlatUIButton with a title and see the title when I run the Simulator. But the button cannot be clicked and is black since all the FlatUI_UIColor.X return null. I really do not know what is going on.

jhondel
  • 76
  • 1
  • 6
  • Is there any difference between the simulator and a device? Sometimes you'll get better error messages in one of them. – Rolf Bjarne Kvinge Apr 07 '14 at 14:38
  • I have the same problem (static = null). The Xamarin.Binding project builds fine, and the project I'm using the .dll in builds for simulator (but crashes when trying to use the static property), and for device it doesn't build. The error is: error MT5211: Native linking failed, undefined Objective-C class: _OBJC_CLASS_$_IZettleSDK. If '_OBJC_CLASS_$_IZettleSDK' is a protocol from a third-party binding, please check that it has the [Protocol] attribute in its api definition file, otherwise verify that all the necessary frameworks have been referenced and native libraries are properly linked in. – Boris Jun 20 '14 at 14:46

2 Answers2

1

Binding methods will typically return null if/when they cannot P/Invoke into the native library. So the problem is probably that you are either not linking with the native library at all in your build (or you are running on Simulator, for example, and the native library does not have i386 support in the FAT binary), or you have the wrong linker flags.

There are two ways to specify the linker flags for a native library for Xamarin.iOS, but I'll walk you through what I think is the easiest way:

If you are using Xamarin Studio and you've created a project using the Objective-C Binding Project template, you can just add the native library (with a .a extension) and Xamarin Studio will create a new source file go along with that with the same base-name of the native library but with a ".linkwith.cs" extension.

What you'll need to do is edit that file and make any adjustments that are needed.

The initial LinkWith attribute should look something like this (this is from memory, so it might not be 100% accurate):

[assembly: LinkWith ("libLibraryName.a", LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator, ForceLoad = true)]

If the library is c++, you'll need to add:

IsCxx = true

You can also specify which Frameworks it depends on:

Frameworks = "UIKit"

If the native library requires linking with other native libraries, you can specify them with the LinkerFlags property (a string).

(You only really need to specify frameworks that the app isn't already going to link with - so you can typically skip Foundation and UIKit, but it doesn't hurt either)

There are other properties that can be set, but I don't remember them all off of the top of my head, so you might want to check the docs for more available properties (Google may turn up additional resources).

jstedfast
  • 35,744
  • 5
  • 97
  • 110
  • Thanks, but still does not work. I have updated my question. I can provide you with the various projects if it helps. – jhondel Apr 07 '14 at 08:46
0

I had the same problem, and the solution was very simple:

  • the interface in the .h header was called 'iZettle'
  • Objective Sharpie translated it into 'IZettle'

I had access to the static properties, but they were null. After days of trying different things, finally we changed the interface name into 'iZettle' (notice the small 'i' at the beginning), so it will match the interface in the header file, and guess what, the static was not null anymore!

Also, as @Rolf Bjarne Kvinge pointed out, when we build for simulator, there were no errors, but when it was built for device, it reported an error, that the 'iZettle' framework was missing.

So maybe you should try and change the public partial interface name to the same as it is in the .h header file in objective-c.

Boris
  • 389
  • 4
  • 12