Introduccion 

En este ocasión quiero mostrar de la manera más simple posible cómo combinar tres de mis tecnologías favoritas: MvvmCross, Xamarin.iOS y Fsharp.

 

Mi intención es mantener el ejemplo lo más simple posible, así que me he basado en el ejemplo más simple de mvvmcross que podemos encontrar en CSharp (link)

You can read this article in English here: jmgomez.me

Configurando el entorno

 

Lo primero que debemos hacer es configurar el entorno, el IDE que utilizaremos será Xamarin Studio. Puedes ver cómo tener FSharp funcionando en iOS aquí  también es posible que necesites descargar el último addin de F# Binding más info aquí (http://stackoverflow.com/questions/16412407/xamarin-studio-fails-to-open-f-files) gracias a @7sharp9

 

Una vez tenemos FSharp funcionando en iOS, tenemos que añadir manualmente las referencias de MvvmCross que podéis encontrar aquí . Actualmente las dll para mac y windows son distintas, en el futuro esto no será así.

 

Manteniendo la convención de MvvmCross, vamos a crear un proyecto de tipo librería para iOS (no portable) llamada ReachingNirvana.Core y con nombre de solución ReachingNirvana. Le añadiremos las siguientes librerías: Cirrious.CrossCore.dll, Cirrous.MvvmCross.dll, Cirrious.MvvmCross.Localization.dll. Lo siguiente será crear un nuevo proyecto llamado ReachingNirvana.Touch del tipo SingleViewApplication. Añadiremos las referencias Cirrious.CrossCore.dll, Cirrious.CrossCore.Touch.dll, Cirrious.MvvmCross.dll,Cirrious.MvvmCross.Touch, Cirrious.MvvmCross.Binding.dll, Cirrious.MvvmCross.Binding.Touch.dll y por último añadimos la referencia de nuestro proyecto ReachingNirvana.Core.

 

 

Haciendo la magia

 

Una vez configurado el entorno debemos añadir un fichero .fs a nuestra solución llamado App.fs con el siguiente contenido:


namespace ReachingNirvana.Core

open System

open Cirrious.CrossCore.IoC

open Cirrious.MvvmCross.ViewModels


type FirstViewModel() = 

    inherit MvxViewModel()

    let mutable hello : string = «Hello MvvmCross»

    member this.Hello

        with get () = hello

        and set (value) =         

            hello <- value 

            this.RaisePropertyChanged(«Hello»)

type App ()  = 

    inherit Cirrious.MvvmCross.ViewModels.MvxApplication()

    override u.Initialize() = 

        u.RegisterAppStart<FirstViewModel>()



 

Como se puede observar hemos definido varios tipos en el mismo fichero, esto no es necesario puedes separar cada tipo en un fichero si lo deseas pero recuerda que el orden de los ficheros en el proyecto importa. Si quieres cambiarlo abre el archivo de configuración del proyecto. 

 

Lo primero que definimos es la clase FirstViewModel que hereda de MvxViewModel y defimos una propiedad llamada Hello con el get y set. Como nota he preferido mantener la simplicidad/legibilidad usando un magic string en «Hello» porque de otra manera habría que hacer castings y queda algo sobrecargada la sintaxis.

 

Por otra parte, definimos el tipo App que hereda de MvxApplication y sobreescribimos el método Initialize, notése que no he registrado nada en el contenedor IoC porque para este ejemplo no es necesario, pero este sería el lugar para hacerlo.

 

Ya hemos terminado con con el proyecto Core, ahora es el turno de Touch. Lo primero es sustituir el código del AppDelegate.fs por este:

 

namespace ReachingNirvana.Touch


open System

open MonoTouch.UIKit

open MonoTouch.Foundation

open Cirrious.MvvmCross.Touch.Platform

open Cirrious.MvvmCross.Touch.Views.Presenters

open Cirrious.MvvmCross.ViewModels

open Cirrious.CrossCore

open ReachingNirvana.Core


type Setup (applicationDelegate:MvxApplicationDelegate, presenter: IMvxTouchViewPresenter) = 

    inherit MvxTouchSetup(applicationDelegate,presenter )

    override u.CreateApp() = 

        new App():> IMvxApplication

 

[<Register («AppDelegate»)>]

type AppDelegate () =

    inherit MvxApplicationDelegate ()


    let window = new UIWindow (UIScreen.MainScreen.Bounds)


    // This method is invoked when the application is ready to run.

    override this.FinishedLaunching (app, options) =

        let presenter = new MvxTouchViewPresenter(this,window)

        let setup = new Setup(this,presenter)

        setup.Initialize()

        let startup = Mvx.Resolve<IMvxAppStart>()

        startup.Start()

        

        window.MakeKeyAndVisible ()

        true


module Main =

    [<EntryPoint>]

    let main args =

        UIApplication.Main (args, null, «AppDelegate»)

        0




 

Aquí estamos definiendo tres clases, Setup AppDelegate y Main. El código es muy similar a la versión de CSharp (aunque mucho más bonito) la diferencia más notable es que debemos hacer casting explícito desde App a IMvxApplication en lugar de implícito. 

 

Por último definiremos nuestra vista, FirstView. Puedes cambiar el nombre a la que proporciona la plantilla de Xamarin Studio o borrarla y añadir una nueva, en el último caso, ¡recuerda que debes cambiar el orden de los ficheros!

 

En FirstView tenemos el siguiente código:

 

namespace ReachingNirvana.Touch

open System

open System.Drawing

open MonoTouch.UIKit

open MonoTouch.Foundation

open ReachingNirvana.Core

open Cirrious.MvvmCross.Binding

open Cirrious.MvvmCross.Binding.BindingContext

open Cirrious.MvvmCross.Touch.Views



[<Register («FirstView»)>]

type FirstView () =

    inherit MvxViewController ()

   

    let  label = new UILabel()

    let textBox = new UITextField() 

    

    // Release any cached data, images, etc that aren’t in use.

    override this.DidReceiveMemoryWarning () =

        base.DidReceiveMemoryWarning ()


    // Perform any additional setup after loading the view, typically from a nib.

    override this.ViewDidLoad () =

        base.ViewDidLoad ()

        this.View.BackgroundColor <- UIColor.White

        label.Frame <- new RectangleF((float32)0,(float32)0,(float32)320,(float32)50)

        this.Add(label)

        textBox.Frame <- new RectangleF((float32)0,(float32)70,(float32)320,(float32)50)

        this.Add(textBox)

        

        let set  =  MvxBindingContextOwnerExtensions.CreateBindingSet<FirstView,FirstViewModel>(this)

        set.Bind(label).To(«Hello») |> ignore

        set.Bind(textBox).To(«Hello») |> ignore

        set.Apply()

        

        

    // Return true for supported orientations

    override this.ShouldAutorotateToInterfaceOrientation (orientation) =

        orientation <> UIInterfaceOrientation.PortraitUpsideDown

 

 

Como se puede observar, es una vista simple que tiene dos controles, un UILabel y un UITextField. En el método ViewDidLoad inicializamos algunas propiedades de los controles y creamos los bindings:

 

CreateBindingSet es un método extensor, que no es más que un método estático que puede ser llamado desde la clase que lo implemente y pásandole por paramétro explicitamente la instancia que lo utiliza. A la hora de crear el binding, tenemos un caso análogo al de llamar a RaisePropertyChanged, bajo mi punto de vista en la versión de FSharp la sintaxis queda muy sobrecargada por lo que he preferido utilizar la sobrecarga con el magic string (internamente mvvmcross llama a este método desde la otra sobrecarga).

 

Ahora ejecutamos y podemos apreciar la armonía de nuestra primera app funcionando con MvvmCross y FSharp. 

 

 

Conclusión

 

Esto ha sido un primer contacto, habría que pensar si nos compensa usar FSharp en lugar de CSharp en todas partes por el poco soporte que tienen las tools (sobre todo por parte de Microsoft), quizá sería más sensato mantener la vista en csharp y cambiar el resto. Lo que está claro es que FSharp tiene muchas ventajas claras sobre CSharp y Csharp también tiene algunas ventajas sobre FSharp, como sabes, de «the right tool for the right job»

 

El código está aquí