Compartir a través de


Información general sobre Unified API

Unified API de Xamarin permite compartir código entre Mac e iOS y admitir aplicaciones de 32 y 64 bits con el mismo binario. Unified API se usa de forma predeterminada en nuevos proyectos de Xamarin.iOS y Xamarin.Mac.

Importante

Classic API de Xamarin, la API anterior a Unified API, ha quedado en desuso.

  • La última versión de Xamarin.iOS que admitía Classic API (monotouch.dll) era Xamarin.iOS 9.10.
  • Xamarin.Mac todavía admite Classic API, pero ya no se actualiza. Dado que está en desuso, los desarrolladores deben mover sus aplicaciones a Unified API.

Actualización de aplicaciones basadas en Classic API

Siga las instrucciones adecuadas para su plataforma:

Sugerencias para actualizar el código a Unified API

Independientemente de las aplicaciones que va a migrar, consulte estas sugerencias para ayudarle a actualizar correctamente a Unified API.

División de bibliotecas

A partir de este punto, nuestras API se mostrarán de dos maneras:

  • Classic API: limitada a 32 bits (solo) y expuesta en los ensamblados monotouch.dll y XamMac.dll.
  • Unified API: admite el desarrollo de 32 y 64 bits con una sola API disponible en los ensamblados Xamarin.iOS.dll y Xamarin.Mac.dll.

Esto significa que los desarrolladores empresariales (que no tienen como destino la App Store) pueden seguir usando las Classic API existentes, ya que las mantendremos para siempre, o bien pueden actualizar a las nuevas API.

Cambios en los espacios de nombres

Para reducir la fricción al compartir código entre nuestros productos para Mac e iOS, estamos cambiando los espacios de nombres de las API en los productos.

Vamos a quitar el prefijo "MonoTouch" de nuestro producto para iOS y "MonoMac" de nuestro producto para Mac en los tipos de datos.

Esto facilita el uso compartido de código entre las plataformas Mac e iOS sin recurrir a compilación condicional y reducirá el ruido en la parte superior de los archivos de código fuente.

  • Classic API: los espacios de nombres usan el prefijo MonoTouch. o MonoMac..
  • Unified API: los espacios de nombres no tienen prefijo.

Valores predeterminados del entorno de ejecución

Unified API usa de forma predeterminada el recolector de elementos no utilizados SGen y el nuevo sistema de recuento de referencias para realizar el seguimiento de la propiedad de los objetos. Esta misma característica se ha migrado a Xamarin.Mac.

Esto resuelve una serie de problemas a los que se enfrentaban los desarrolladores con el sistema antiguo y también facilita la administración de memoria.

Tenga en cuenta que es posible habilitar el nuevo recuento de referencias (refcount) incluso para la Classic API, pero el valor predeterminado es conservador y no requiere que los usuarios realicen ningún cambio. Con Unified API, hemos tomado la oportunidad de cambiar el valor predeterminado y proporcionar a los desarrolladores todas las mejoras mientras refactorizan y vuelven a probar su código.

Cambios en la API

Unified API quita los métodos en desuso y había algunas instancias en las que había errores tipográficos en los nombres de API cuando estaban enlazados a los espacios de nombres MonoTouch y MonoMac originales en las Classic API. Estas instancias se han corregido en las nuevas Unified API y tendrán que actualizarse en las aplicaciones de componentes de iOS y Mac. Esta es una lista de los cambios más comunes que podría encontrarse:

Nombre del método de Classic API Nombre del método de Unified API
UINavigationController.PushViewControllerAnimated() UINavigationController.PushViewController()
UINavigationController.PopViewControllerAnimated() UINavigationController.PopViewController()
CGContext.SetRGBFillColor() CGContext.SetFillColor()
NetworkReachability.SetCallback() NetworkReachability.SetNotification()
CGContext.SetShadowWithColor CGContext.SetShadow
UIView.StringSize UIKit.UIStringDrawing.StringSize

Para obtener una lista completa de los cambios al pasar de la Classic API a Unified API, consulte nuestra documentación sobre las diferencias entre Classic API (monotouch.dll) y Unified API (Xamarin.iOS.dll).

Actualización a Unified API

Varias API antiguas, rotas o en desuso en Classic API no están disponibles en Unified API. Puede ser más fácil corregir las advertencias CS0616 antes de iniciar la actualización (manual o automatizada), ya que tendrá el mensaje del atributo [Obsolete] (parte de la advertencia) que le guiará a la API correcta.

Tenga en cuenta que hemos publicado un informe de diferencias de los cambios entre Classic API y Unified API que se pueden usar antes o después de las actualizaciones del proyecto. La corrección de las llamadas obsoletas en Classic API suele ahorrar tiempo (menos búsquedas de documentación).

Siga estas instrucciones para actualizar las aplicaciones iOS existentes o las aplicaciones Mac a Unified API. Revise el resto de la página y estas sugerencias para obtener información adicional sobre cómo migrar el código.

NuGet

Los paquetes NuGet que admitía anteriormente Xamarin.iOS por medio de la Classic API publicaban sus ensamblados mediante el moniker Monotouch10 de la plataforma.

Unified API presenta un nuevo identificador de plataforma para paquetes compatibles: Xamarin.iOS10. Los paquetes NuGet existentes deberán actualizarse para agregar compatibilidad con esta plataforma mediante la compilación en la Unified API.

Importante

Si recibe un error de tipo "Error 3: No se puede incluir 'monotouch.dll' y 'Xamarin.iOS.dll' en el mismo proyecto de Xamarin.iOS: se hace referencia a 'Xamarin.iOS.dll' explícitamente, mientras que se hace referencia a 'monotouch.dll' mediante 'xxx, Version=0.0.000, Culture=neutral, PublicKeyToken=null'" después de convertir la aplicación a las Unified API, normalmente se debe a tener un componente o un paquete NuGet en el proyecto que no se ha actualizado a Unified API. Deberá quitar el componente o NuGet existente, actualizar a una versión que admita las Unified API y realizar una compilación limpia.

Compatibilidad con 64 bits

Para obtener información sobre cómo admitir aplicaciones de 32 y 64 bits y sobre marcos, consulte Consideraciones sobre las plataformas de 32 y 64 bits.

Nuevos tipos de datos

La diferencia esencial es que las API de Mac e iOS usan tipos de datos específicos de la arquitectura que siempre son de 32 bits en plataformas de 32 bits y de 64 bits en plataformas de 64 bits.

Por ejemplo, Objective-C asigna el tipo de datos NSInteger a int32_t en sistemas de 32 bits y a int64_t en sistemas de 64 bits.

Para igualar este comportamiento, en nuestra Unified API, reemplazamos los usos anteriores de int (que en .NET se define siempre como System.Int32) por un nuevo tipo de datos: System.nint. En este caso, "n" significa "nativo", por lo que se trata del tipo entero nativo de la plataforma.

Estamos introduciendo nint, nuint y nfloat, y proporcionamos tipos de datos basados en ellos cuando es necesario.

Para obtener más información sobre estos cambios del tipo de datos, consulte el artículo Tipos nativos para iOS y macOS.

Detección de la arquitectura de las aplicaciones de iOS

Es posible que haya situaciones en las que su aplicación necesite saber si se ejecuta en un sistema iOS de 32 o 64 bits. El código siguiente se puede usar para comprobar la arquitectura:

if (IntPtr.Size == 4) {
    Console.WriteLine ("32-bit App");
} else if (IntPtr.Size == 8) {
    Console.WriteLine ("64-bit App");
}

Matrices y System.Collections.Generic

Dado que los indexadores de C# esperan un tipo de int, tendrá que convertir explícitamente los valores nint en valores int para acceder a los elementos de una colección o matriz. Por ejemplo:

public List<string> Names = new List<string>();
...

public string GetName(nint index) {
    return Names[(int)index];
}

Este es el comportamiento esperado, ya que la conversión de int a nint provoca pérdidas en 64 bits, no se realiza una conversión implícita.

Conversión de DateTime a NSDate

Al usar las Unified API, la conversión implícita de los valores DateTime a valores NSDate ya no se realiza. Estos valores deberán convertirse explícitamente de un tipo a otro. Los siguientes métodos de extensión se pueden usar para automatizar este proceso:

public static DateTime NSDateToDateTime(this NSDate date)
{
    // NSDate has a wider range than DateTime, so clip
    // the converted date to DateTime.Min|MaxValue.
    double secs = date.SecondsSinceReferenceDate;
    if (secs < -63113904000)
        return DateTime.MinValue;
    if (secs > 252423993599)
        return DateTime.MaxValue;
    return (DateTime) date;
}

public static NSDate DateTimeToNSDate(this DateTime date)
{
    if (date.Kind == DateTimeKind.Unspecified)
        date = DateTime.SpecifyKind (date, /* DateTimeKind.Local or DateTimeKind.Utc, this depends on each app */)
    return (NSDate) date;
}

API en desuso y errores tipográficos

Dentro de la Classic API de Xamarin.iOS (monotouch.dll), el atributo [Obsolete] se usaba de dos maneras diferentes:

  • API de iOS en desuso: esto es cuando Apple le sugiere que deje de usar una API porque se va a sustituir por una más reciente. La Classic API sigue siendo correcta y a menudo necesaria (si se admite la versión anterior de iOS). Dicha API y el atributo [Obsolete] se incluyen en los nuevos ensamblados de Xamarin.iOS.
  • API incorrecta: algunas API tenían errores tipográficos en sus nombres.

Para los ensamblados originales (monotouch.dll y XamMac.dll) hemos mantenido el código antiguo disponible por motivos de compatibilidad, pero se han quitado de los ensamblados de Unified API (Xamarin.iOS.dll y Xamarin.Mac).

Subclases de NSObject: .ctor(IntPtr)

Cada subclase NSObject tiene un constructor que acepta un objeto IntPtr. Así es como podemos crear instancias de una nueva instancia administrada desde un manipulador ObjC nativo.

En Classic API, se trataba de un constructor public. Sin embargo, era fácil hacer un uso incorrecto de esta característica en el código de usuario, por ejemplo, crear varias instancias administradas para una sola instancia de ObjC o crear una instancia administrada que carecía del estado administrado esperado (para las subclases).

Para evitar esos tipos de problemas, los constructores IntPtr ahora están protegidos (protected) en Unified API para que solo se usen para la creación de subclases. Esto garantizará que la API correcta o segura se use para crear una instancia administrada a partir de manipuladores, por ejemplo.

var label = Runtime.GetNSObject<UILabel> (handle);

Esta API devolverá una instancia administrada existente (si la hay) o creará una nueva (cuando sea necesario). Ya está disponible tanto en Classic API como en Unified API.

Tenga en cuenta que ahora .ctor(NSObjectFlag) también está protected, pero rara vez se usa fuera de la creación de subclases.

Reemplazo de NSAction por Action

Con las Unified API, el tipo NSAction se ha reemplazado por el tipo Action estándar de .NET. Se trata de una gran mejora porque Action es un tipo de .NET común, mientras que NSAction era específico de Xamarin.iOS. Ambos hacen exactamente lo mismo, pero eran tipos distintos e incompatibles y requerían que se escribiera más código para lograr el mismo resultado.

Por ejemplo, si la aplicación de Xamarin existente incluía el código siguiente:

UITapGestureRecognizer singleTap = new UITapGestureRecognizer (new NSAction (delegate() {
    ShowDropDownAnimated (tblDataView);
}));

Ahora se puede reemplazar por una expresión lambda simple:

UITapGestureRecognizer singleTap = new UITapGestureRecognizer (() => ShowDropDownAnimated(tblDataView));

Anteriormente, esto hubiera generado un error del compilador, ya que Action no se puede asignar a NSAction, pero dado que ahora UITapGestureRecognizer toma un elemento Action en lugar de NSAction, es un comportamiento válido en las Unified API.

Reemplazo de los delegados personalizados por Action<T>

En Unified API, algunos de los delegados de .Net simples (por ejemplo, un parámetro) se han reemplazado por Action<T>. Por ejemplo,

public delegate void NSNotificationHandler (NSNotification notification);

Ahora esto se puede usar como Action<NSNotification>. Esto promueve la reutilización del código y reduce la duplicación de código dentro de Xamarin.iOS y de sus propias aplicaciones.

Reemplazo de Task<bool> por Task<Boolean,NSError>>

En las Classic API, había algunas API asincrónicas que devolvían Task<bool>. Sin embargo, algunas se usaban cuando NSError formaba parte de la firma, por ejemplo, bool ya era true y era necesario detectar una excepción para obtener NSError.

Dado que algunos errores eran muy comunes y el valor devuelto no era útil, este patrón se ha cambiado en Unified API para devolver Task<Tuple<Boolean,NSError>>. Esto le permite comprobar el éxito y cualquier error que pueda haber ocurrido durante la llamada asincrónica.

NSString frente a string

En algunos casos, era necesario cambiar algunas constantes de string a NSString, por ejemplo, UITableViewCell.

Clásico

public virtual string ReuseIdentifier { get; }

Unified API

public virtual NSString ReuseIdentifier { get; }

En general, preferimos el tipo System.String de .NET. Sin embargo, a pesar de las directrices de Apple, algunas API nativas comparan punteros constantes (no la propia cadena) y esto solo puede funcionar cuando exponemos las constantes como NSString.

Protocolos Objective-C

El marco MonoTouch original no tenía compatibilidad total con los protocolos ObjC, y se agregaban algunas API no óptimas para admitir el escenario más común. Esta limitación ya no existe, pero para la compatibilidad con versiones anteriores, varias API se mantienen en monotouch.dll y XamMac.dll.

Estas limitaciones se han quitado y limpiado en las Unified API. La mayoría de los cambios tendrán un aspecto similar al siguiente:

Clásico

public virtual AVAssetResourceLoaderDelegate Delegate { get; }

Unified API

public virtual IAVAssetResourceLoaderDelegate Delegate { get; }

El prefijo I significa que Unified API expone una interfaz, en lugar de un tipo específico, para el protocolo ObjC. Esto facilitará los casos en los que no quiera crear una subclase del tipo específico que proporciona Xamarin.iOS.

También permite que algunas API sean más precisas y fáciles de usar, por ejemplo:

Clásico

public virtual void SelectionDidChange (NSObject uiTextInput);

Unified API

public virtual void SelectionDidChange (IUITextInput uiTextInput);

Esta API ahora es más fácil de usar sin tener que consultar la documentación, y la finalización del código IDE le proporcionará sugerencias más útiles basadas en el protocolo o la interfaz.

Protocolo NSCoding

Nuestro enlace original incluía un constructor .ctor(NSCoder) para cada tipo, incluso si no admitía el protocolo NSCoding. Un único método Encode(NSCoder) estaba presente en NSObject codificar el objeto. Pero este método solo funcionaba si la instancia se ajustaba al protocolo NSCoding.

En Unified API, hemos corregido esto. Los nuevos ensamblados solo tendrán .ctor(NSCoder) si el tipo se ajusta a NSCoding. Además, estos tipos ahora tienen un método Encode(NSCoder) que se ajusta a la interfaz INSCoding.

Bajo impacto: en la mayoría de los casos, este cambio no afectará a las aplicaciones, ya que no se pueden usar los antiguos constructores eliminados.

Sugerencias adicionales

Otros cambios que se deben tener en cuenta se explican en el artículo Sugerencias para actualizar el código a Unified API.