Hacer referencia a bibliotecas nativas en Xamarin.iOS

Xamarin.iOS admite la vinculación con bibliotecas y Objective-C bibliotecas nativas de C. En este documento se describe cómo vincular las bibliotecas nativas de C con el proyecto de Xamarin.iOS. Para obtener información sobre cómo hacer lo mismo con Objective-C bibliotecas, consulte el documento Enlace Objective-C Tipo.

Crear bibliotecas nativas universales (i386, ARMv7 y ARM64)

A menudo, es conveniente compilar las bibliotecas nativas para cada una de las plataformas admitidas para el desarrollo de iOS (i386 para el simulador y ARMv7/ARM64 para los propios dispositivos). Si ya tiene un proyecto Xcode para la biblioteca, esto es muy sencillo.

Para compilar la versión i386 de su biblioteca nativa, ejecute el siguiente comando desde un terminal:

/Developer/usr/bin/xcodebuild -project MyProject.xcodeproj -target MyLibrary -sdk iphonesimulator -arch i386 -configuration Release clean build

Esto dará como resultado una biblioteca estática nativa en MyProject.xcodeproj/build/Release-iphonesimulator/. Copie (o mueva) el archivo de la biblioteca (libMyLibrary.a) a un lugar seguro para su uso posterior, dándole un nombre único (como libMyLibrary-i386.a) para que no entre en conflicto con las versiones arm64 y armv7 de la misma biblioteca que compilará a continuación.

Para compilar la versión ARM64 de la biblioteca nativa, ejecute el siguiente comando:

/Developer/usr/bin/xcodebuild -project MyProject.xcodeproj -target MyLibrary -sdk iphoneos -arch arm64 -configuration Release clean build

Esta vez, la biblioteca nativa compilada se ubicará en MyProject.xcodeproj/build/Release-iphoneos/. Una vez más, copie (o mueva) este archivo a una ubicación segura y cámbiele el nombre a algo como libMyLibrary-arm64.a para que no entre en conflicto.

Ahora compile la versión ARMv7 de la biblioteca:

/Developer/usr/bin/xcodebuild -project MyProject.xcodeproj -target MyLibrary -sdk iphoneos -arch armv7 -configuration Release clean build

Copie (o mueva) el archivo de biblioteca resultante a la misma ubicación en la que movió las otras 2 versiones de la biblioteca, cambiando el nombre a algo como libMyLibrary-armv7.a.

Para crear un binario universal, puede usar la herramienta lipo de este modo:

lipo -create -output libMyLibrary.a libMyLibrary-i386.a libMyLibrary-arm64.a libMyLibrary-armv7.a

Esto crea libMyLibrary.a, que será una biblioteca universal (fat) que se podrá usar para todos los destinos de desarrollo de iOS.

Falta la arquitectura necesaria i386

Si recibe un mensaje does not implement methodSignatureForSelector o does not implement doesNotRecognizeSelector en la salida del runtime al intentar consumir una biblioteca Objective-C en el simulador de iOS, es probable que la biblioteca no se compile para la arquitectura i386 (consulte la sección Compilar bibliotecas nativas universales anterior).

Para comprobar las arquitecturas que admite una biblioteca determinada, use el siguiente comando en el terminal:

lipo -info /full/path/to/libraryname.a

Donde /full/path/to/ es la ruta de acceso completa a la biblioteca que se consume y libraryname.a es el nombre de la biblioteca en cuestión.

Si tiene el origen de la biblioteca, deberá compilarlo y agruparlo también para la arquitectura i386, si quiere probar la aplicación en el simulador de iOS.

Vincular la biblioteca

Cualquier biblioteca de terceros que consuma debe estar vinculada estáticamente con la aplicación.

Si desea vincular estáticamente la biblioteca "libMyLibrary.a" que obtuvo de Internet o compilar con Xcode, necesita hacer lo siguiente:

  • Incluir la biblioteca en el proyecto
  • Configurar Xamarin.iOS para vincular la biblioteca
  • Acceder a los métodos desde la biblioteca.

Para incluir la biblioteca en el proyecto, seleccione el proyecto en el Explorador de soluciones y presione Command+Option+a. Vaya a libMyLibrary.a y agréguelo al proyecto. Cuando se le solicite, indique a Visual Studio para Mac o Visual Studio que lo copie en el proyecto. Después de agregarlo, busque el archivo libFoo.a en el proyecto, haga clic con el botón derecho sobre él y establezca la Acción de compilación en Ninguna.

Para configurar Xamarin.iOS para vincular la biblioteca, en las opciones del proyecto para el ejecutable final (no la biblioteca en sí, sino el programa final) debe agregar en el argumento Extra de la compilación de iOS (forma parte de las opciones del proyecto) la opción "-gcc_flags" seguida de una cadena entre comillas que contenga todas las bibliotecas adicionales necesarias para el programa, por ejemplo:

-gcc_flags "-L$(MSBuildProjectDirectory) -lMylibrary -force_load $(MSBuildProjectDirectory)/libMyLibrary.a"

En el ejemplo anterior se vinculará libMyLibrary.a

Puede usar -gcc_flags para especificar cualquier conjunto de argumentos de línea de comandos para pasar al compilador GCC que se usa para hacer el vínculo final del ejecutable. Por ejemplo, esta línea de comandos también hace referencia al marco CFNetwork:

-gcc_flags "-L$(MSBuildProjectDirectory) -lMylibrary -lSystemLibrary -framework CFNetwork -force_load $(MSBuildProjectDirectory)/libMyLibrary.a"

Si la biblioteca nativa contiene código de C++, también debe pasar la marca -cxx en los "Argumentos adicionales" para que Xamarin.iOS sepa usar el compilador correcto. En C++ las opciones anteriores serían similares a las siguientes:

-cxx -gcc_flags "-L$(MSBuildProjectDirectory) -lMylibrary -lSystemLibrary -framework CFNetwork -force_load $(MSBuildProjectDirectory)/libMyLibrary.a"

Acceder a métodos de C desde C#

Hay dos tipos de bibliotecas nativas disponibles en iOS:

  • Bibliotecas compartidas que forman parte del sistema operativo.

  • Bibliotecas estáticas que se envían con la aplicación.

Para acceder a los métodos definidos en cualquiera de ellas, use la funcionalidad P/Invoke de Mono, que es la misma tecnología que se usaría en .NET, es decir, aproximadamente:

  • Determinar qué función de C desea invocar
  • Determinar su firma
  • Determinar en qué biblioteca reside
  • Escribir la declaración P/Invoke adecuada

Cuando use P/Invoke, debe especificar la ruta de acceso de la biblioteca con la que se está vinculando. Al usar bibliotecas compartidas de iOS, puede codificar de forma rígida la ruta de acceso o puede usar las constantes de conveniencia que hemos definido en Constants, estas constantes deben abarcar las bibliotecas compartidas de iOS.

Por ejemplo, si desea invocar el método UIRectFrameUsingBlendMode de la biblioteca UIKit de Apple que tiene esta firma en C:

void UIRectFrameUsingBlendMode (CGRect rect, CGBlendMode mode);

La declaración P/Invoke tendría este aspecto:

[DllImport (Constants.UIKitLibrary,EntryPoint="UIRectFrameUsingBlendMode")]
public extern static void RectFrameUsingBlendMode (RectangleF rect, CGBlendMode blendMode);

Constants.UIKitLibrary es simplemente una constante definida como "/System/Library/Frameworks/UIKit.framework/UIKit", EntryPoint nos permite especificar opcionalmente el nombre externo (UIRectFramUsingBlendMode) mientras que expone un nombre diferente en C#, el RectFrameUsingBlendMode más corto.

Acceder a C Dylibs

Si necesita consumir una instancia de C Dylib en la aplicación Xamarin.iOS, es necesario realizar una configuración adicional antes de llamar al atributo DllImport.

Por ejemplo, si tenemos un Animal.dylib con un método Animal_Version al que llamaremos en nuestra aplicación, tenemos que informar a Xamarin.iOS de la ubicación de la biblioteca antes de intentar consumirla.

Para ello, edite el archivo Main.CS y haga que tenga un aspecto similar al siguiente:

static void Main (string[] args)
{
    // Load Dylib
    MonoTouch.ObjCRuntime.Dlfcn.dlopen ("/full/path/to/Animal.dylib", 0);

    // Start application
    UIApplication.Main (args, null, "AppDelegate");
}

Donde /full/path/to/ es la ruta de acceso completa a la instancia de Dylib que se consume. Con este código, podemos vincular al método Animal_Version de la siguiente manera:

[DllImport("Animal.dylib", EntryPoint="Animal_Version")]
public static extern double AnimalLibraryVersion();

Bibliotecas estáticas

Puesto que solo se pueden usar bibliotecas estáticas en iOS, no hay ninguna biblioteca compartida externa con la que vincular, por lo que el parámetrode ruta de acceso en el atributo DllImport debe usar el nombre especial __Internal (tenga en cuenta los caracteres de subrayado doble al principio del nombre) en lugar del nombre de la ruta de acceso.

Esto obliga a DllImport a buscar el símbolo del método al que se hace referencia en el programa actual, en lugar de intentar cargarlo desde una biblioteca compartida.