Inyección de dependencia Xamarin.Forms

1.Símbolos de compilación condicional en proyectos compartidos (Shared Projects)

Un punto importante es que aún en el proyecto Portable podemos especificar un comportamiento diferente para cada plataforma utilizando la directriz #ifdef, por ejemplo  #ifdef __ANDROID__ significa que el siguiente código será válido sólo para Android desde el momento de la compilación, de igual manera usando #if __IOS__ será válido para iOS.

Voy a explicar qué son y cómo se utilizan los Símbolos de Compilación Condicional que tanto se emplean en Xamarin y en las Apps de Windows. Para los que no lo sepan, Xamarin es un framework para desarrollo de apliaciones móviles Cross-Platform en C#.

¿Qué son?
Son unas etiquetas que describen las distintas plataformas compatibles con un proyecto Cross-Platform.

¿Cuándo usarlos?
Es recomendable usarlos cuando sólo hay diferencias muy pequeñas entre las distintas plataformas y no merece la pena extraer una clase, proyecto o solución para cada plataforma.

En Xamarin están disponibles los siguientes símbolos de compilación condicional:

#if __IOS__
// Código específico para iOS
#endif

#if __ANDROID__
// Código específico para Android
#endif

#if __ANDROID_11__
 // Código específico para Android 3.0 o posterior
#endif

En soluciones que compartan código de distintas Windows Apps

#if WINDOWS_APP
 // Código para una Windows Store app
#else
 // Código para una Windows Phone app
#endif

#if NETFX_CORE
    // Código para una Windows Store app.
#else
    // Código para una Windows Phone Silverlight app*.
#endif

#if NETFX_CORE
    #if WINDOWS_APP
     // Código para una Windows Store app.
    #else
     // Código para una Windows Phone Store app.
    #endif 
#else
    // Código para una Windows Phone Silverlight app.
#endif

Una aplicación Windows Phone Silverlight no es más que un Frank Einstein de una Aplicación en Windows Phone 8 con la posibilidad de acceder a nuevas características de las aplicaciones Windows Phone Store sin necesidad de modificar el código existente en Windows Phone 8.

Para definir los simbolos, click Derecho en el proyecto > Propiedades > en las configuraciones seleccionar "Compilación" > en simbolos de compilacion condicional colocar __MOBILE__;__IOS__;__ANDROID__;

image

Ejemplo 1: Escribiendo mensajes en la consola de cada tipo de dispositivo

#if __IOS__
    Console.WriteLine ("__IOS__ esta definido");
#endif 
#if __ANDROID__
    Console.WriteLine ("__ANDROID__ esta definido");
#endif

Ejemplo 2: Otro ejemplo que se usa bastante en Android, IOS tienen distinto modo de acceder al directorio en el que guardar la base de datos. Gracias a los símbolos de compilación condicional podemos utilizar un sólo método para acceder a esta ruta. Una vez tenemos la ruta a la base de datos, las operaciones con Xamarin Forms se hacen igual para todas las plataformas usando la PCL de SQLite disponible en NuGet.

string DatabasePath {
    get {
        var sqliteFilename = "SQLite.db3";
        #if __IOS__
        string documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal); // Documents folder
        string libraryPath = Path.Combine (documentsPath, "..", "Library"); // Library folder
        var path = Path.Combine(libraryPath, sqliteFilename);
        #else
        #if __ANDROID__
        string documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal); // Documents folder
        var path = Path.Combine(documentsPath, sqliteFilename);
        #else
        // WinPhone
        var path = Path.Combine(ApplicationData.Current.LocalFolder.Path, sqliteFilename);;
        #endif
        #endif
        return path;
    }
}

2.Device y TargetPlatform

Otra forma es el uso de la propiedad Xamarin.Forms.Device.OS que obtiene el sistema operativo que se esta ejecutando  y la enumeración Xamarin.Forms.TargetPlatform  indica el actual tipo de formularios de OS. En el siguiente código se muestra como hacer las comparaciones para saber que OS se esta ejecutando.

public void Option()
{
            var osName = string.Empty;
            if (Device.OS==TargetPlatform.Android)
                osName = "Hola Droid";
            if (Device.OS==TargetPlatform.iOS)
                osName = "Hola iOS";
            if (Device.OS == TargetPlatform.WinPhone)
                osName = "Hola WinPhone";
}

3.Definición de Interfaces

La forma de lograr esto es que en el proyecto portable definimos las interfaces de nuestras clases, y éstas se implementarán en cada plataforma (iOS y Android para nuestro caso), utilizando DependencyService de Xamarin para instanciar las implementaciones o si se requiere algo mucho más robusto podemos utilizar Unity , TinyIOC,  Autofac entre otras que se pueden obtener desde Nuget.

Las principales clases candidatas a ser implementadas por medio de inyección de dependencias son las que utilizarán recursos propiamente de la plataforma como: acceder a la ruta de la aplicación en el dispositivo móvil para almacenar un archivo, acceso a los recursos de redes, preferencias,  creación y acceso a una base de datos de SQLite, preferencias de la aplicación en el dispositivo, personalizar un control como un botón, un grid, procesamiento de una imagen, etcétera.

DependencyService es el medio por el cual podemos resolver las interfaces cuya implementación se encuentra en cada plataforma. Al utilizar:

[assembly:Dependency(typeof( Clase)) estamos indicando a .Net que esa clase podrá resolverse mediante DependencyService tomando la implementación en la plataforma en ejecución. por ejemplo: si nuestro proyecto se llama "MiApp" y el tipo de OS que vamos a realizar la implementacion es Android  y nombre de la Interfaz a implementar es "IAccesoDB" se codificara de la siguiente manera [assembly:Dependency(typeof(MiApp.Droid.AccesoDB))]

A continuación mostraré un ejemplo para realizar la funcionalidad de localización de recursos para soportar multilenguaje.

1. En el proyecto portable declaramos un interfaz llamado ILocalizeService con la firma de métodos a implementar.

using System.Globalization;

namespace App1_PrimeraApp
{
    public interface ILocalizeService
    {
        CultureInfo GetCurrentCultureInfo();
        string GetCurrent();
        void SetLocate();
    }
}

2.-A continuación se codificara la implementación puntual de esta funcionalidad en cada plataforma. Se crea una clase con el nombre de "LocalizeService" en el proyecto de Android ".Droid" que tendrá el siguiente código

using System.Globalization;
using System.Threading;
using Xamarin.Forms;

[assembly: Dependency(typeof(App1_PrimeraApp.Droid.LocalizeService))]
namespace App1_PrimeraApp.Droid
{
    public class LocalizeService:ILocalizeService
    {
        public CultureInfo GetCurrentCultureInfo()
        {
            var language = Java.Util.Locale.Default.ToString().Replace("_", "-");

            var cultureInfo = new System.Globalization.CultureInfo(language);
            return cultureInfo;
        }

        public string GetCurrent()
        {
            var locale = Java.Util.Locale.Default;
            var language = locale.Language.Replace("_", "-");
            var netLocate = locale.ToString().Replace("_", "-");

            var cultureInfo=new System.Globalization.CultureInfo(netLocate);
            Thread.CurrentThread.CurrentCulture = cultureInfo;
            Thread.CurrentThread.CurrentUICulture = cultureInfo;
            return netLocate;
        }

        public void SetLocate()
        {
            var locate = Java.Util.Locale.Default.ToString().Replace("_", "-");
            var cultureInfo=new System.Globalization.CultureInfo(locate);
            Thread.CurrentThread.CurrentCulture = cultureInfo;
            Thread.CurrentThread.CurrentUICulture = cultureInfo;
        }

    }
}

3. Ahora se codificara la implementación en la plataforma de iOS, de igual forma se crea una clase con el nombre "LocalizeService" que tendrá el siguiente Código.

using System.Globalization;
using System.Threading;
using Foundation;
using Xamarin.Forms;

[assembly: Dependency(typeof(App1_PrimeraApp.iOS.LocalizeService))]
namespace App1_PrimeraApp.iOS
{
   public class LocalizeService:ILocalizeService
    {
       public CultureInfo GetCurrentCultureInfo()
       {
           var language = NSLocale.CurrentLocale.LocaleIdentifier.Replace("_", "-");

           var cultureInfo = new System.Globalization.CultureInfo(language);
           return cultureInfo;
       }

       public string GetCurrent()
       {
           //To get the current region and language, will return "en_US"
           var locale = NSLocale.CurrentLocale.LocaleIdentifier;
           var netLocate = locale.ToString().Replace("_", "-");
           var cultureInfo = new System.Globalization.CultureInfo(netLocate);

           Thread.CurrentThread.CurrentCulture =cultureInfo;
           Thread.CurrentThread.CurrentUICulture = cultureInfo;

           return netLocate;
       }

       public void SetLocate()
       {
           var locate = NSLocale.CurrentLocale.LocaleIdentifier.Replace("_", "-");
           var cultureInfo = new System.Globalization.CultureInfo(locate);

           Thread.CurrentThread.CurrentCulture = cultureInfo;
           Thread.CurrentThread.CurrentUICulture = cultureInfo;
       }
    }
}

Teniendo estas implementaciones, basta colocar desde el código portable la llamada de un método como:

  var language = DependencyService.Get<ILocalizeService>().GetCurrent();

One thought on “Inyección de dependencia Xamarin.Forms

Deja un comentario