Tech163
Tech163

Reputation: 4286

How do I wrap Objective-C library in Xamarin.iOS?

The header file includes the following:

#include <Foundation/Foundation.h>

FOUNDATION_EXPORT NSString* GoGopherExecute(NSString* action, NSString* data);

I have taken a look at How do I correctly wrap native c library in Xamarin.iOS, which seems closest to what I am trying to do. I've also seen the resources on the Xamarin website https://developer.xamarin.com/guides/cross-platform/macios/binding/objective-c-libraries/. I have the following in my ApiDefinition.cs

[DllImport("Gopher.a", 
    EntryPoint="GoGopherExecute", 
    CallingConvention = CallingConvention.Cdecl)]
public static NSString GoExecute(NSString action, NSString data);

The error I am getting is "Unexpected symbol 'NSString', expecting 'class', 'delegate', 'enum', 'interface', 'partial', or 'struct'". What should I have in ApiDefinition.cs?

Upvotes: 2

Views: 1393

Answers (2)

dalexsoto
dalexsoto

Reputation: 3412

While you are close, you do need some additional steps in order to have it fully working. But first you need to grasp some concepts of a Xamarin.iOS binding project.

Here is the Xamarin.iOS binding project structure:

enter image description here

Most of the time you will only need the two files that comes with the project template (I manually added Extras.cs):

Quote from Xamarin Documentation

ApiDefinition.cs: will only contain namespaces and interface definitions (with any members that an interface can contain) and should not contain classes, enumerations, delegates or structs. The API definition file is merely the contract that will be used to generate the API.

StructsAndEnums.cs: any enums, types, structs required by the API definition file.

So, one thing to note in the ApiDefinition is that it can only contain ObjC definitions so your code above must be added into an additional c# code file since it is a c function, just add a new class to your binding project I tend to name this file Extras.cs (You can name it whatever you feel like is best).

// Extras.cs contents
using System;
using System.Runtime.InteropServices;
using Foundation;
using ObjCRuntime;

namespace FooBinding {
    public static class GoGopherCFuncs {
        // FOUNDATION_EXPORT NSString* GoGopherExecute(NSString* action, NSString* data);
        [DllImport ("__Internal", EntryPoint= "GoGopherExecute")]
        static extern IntPtr _GoExecute (IntPtr action, IntPtr data);

        public static string GoExecute (string action, string data)
        {
            // Avoid creating any unused managed refs of NSString and get a handle directly
            // from our managed NET strings
            IntPtr actionPtr = NSString.CreateNative (action);
            IntPtr dataPtr = NSString.CreateNative (data);

            IntPtr ptr = _GoExecute (actionPtr, dataPtr);

            // Manually release our native NSString handles
            NSString.ReleaseNative (actionPtr);
            NSString.ReleaseNative (dataPtr);

            // Return a NET string from our unmanaged handle returned by _GoExecute.
            return (string) Runtime.GetNSObject<NSString> (ptr);
        }
    }
}

So the code above should be enough to solve your problem and get you started into binding any additional c API.

There is an additional thing you need to modify in the DllImportattribute, you need to change the dllName to __Internal this is because it is likely that Gopher.a is a static library (You can check this other answer in order to confirm if it is a static library) so it will be "merged" into your main executable so you only need to add Gopher.a to your binding project as described in the Xamarin Documentation.

Quote from Xamarin Documentation

To complete this binding, you should add the native library to the project. You can do this by adding the native library to your project, either by dragging and dropping the native library from Finder onto the project in the solution explorer ...

I really encourage you to read the Xamarin Documentation :)

Hope this helps!

Upvotes: 3

Giorgi
Giorgi

Reputation: 30873

It looks like you put the method outside of any class. You should move to a class because in C# a method cannot exist without a class.

public static class GopherInterop
{
    [DllImport("Gopher.a", EntryPoint="GoGopherExecute", CallingConvention = CallingConvention.Cdecl)]
    public static NSString GoExecute(NSString action, NSString data);
}

Upvotes: 1

Related Questions