Reaching the Nirvana: MvvmCross + Xamarin.iOS + FSharp

Reaching the Nirvana: MvvmCross + Xamarin.iOS + FSharp

Introduction:

This time I want to show you how to combine three of my favourite technologies in a simple way: MvvmCross, Xamarin.iOS y Fsharp  

 

Disclaimer: I'm not a FSharp expert. I just fell in love with the first line I wrote. There is so much power behind the scenes.

 

I want to keep the example as simple as possible so I based it on the easiest example of mvvmcross that you can find in CSharp (link). 

 

 

Setting up the enviroment

 

The first thing that we have to do is to configure the environment, in order to do that, you need to have FSharp installed and working in iOS. You can see how do that here (http://fsharp.org/use/ios/). Perhaps you also need the latest version of the addin F# binding, more info here thanks to @7sharp9.

 

Once we have FSharp working on iOS, we have to manually add the references of MvvmCross that can be found here. Note that currently the libraries for osx and windows aren't the same.

 

In order to keep the convention of MvvmCross, we are going to make a project of type Library for iOS called ReachingNirvana.Core and the solution name will be ReachingNirvana. We'll also add the followed libraries: Cirrious.CrossCore.dll, Cirrous.MvvmCross.dll, Cirrious.MvvmCross.Localization.dll. The next thing that we have to do is to make a new project called ReachingTheNivana.Touch of type SingleViewApplication. And we also add the references Cirrious.CrossCore.dll, Cirrious.CrossCore.Touch.dll, Cirrious.MvvmCross.dll,Cirrious.MvvmCross.Touch, Cirrious.MvvmCross.Binding.dll, Cirrious.MvvmCross.Binding.Touch.dll and finally the reference for our ReachingNirvana.Core project.

 

 

Doing the magic

 

Once we have configured the environment we should add a file with the extension .fs to our application with the following content:

 

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>()

 


 

As you can see we defined some types in the same file, that isn't necessary, if you wish you can separate each type in an individual file, but remember that the order of the file matters. So if you want to change it, you also need to change the file project.

 

The first thing we defined is the FirstViewModel class which inherits from MvxViewModel and we also defined a property called Hello with its get and set. I prefer to keep the legibility, using a string instead of a func, because the sintax to do that in fsharp is a bit overload because of the explicit casting.

 

Moreover we defined the type App which inherits from MvxApplication and we also override the Initialize method. Note that we don't register anything in the IoC container because it isn't necessary for this example, but if you want to, this is the right place to do it.

 

Now we have done the Core project, so it's time to do the Touch project. The first thing that we have to do is to replace the code in the AppDelegate.fs file with this one:

 

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

 


 

We defined three classes: Setup, AppDelegate and Main. The code is very similar to the CSharp version (but it has more beauty), the main difference is that we need to do an explicit casting from App to IMvxApplication instead of an implicit one. 

 

Finally, we'll need to define our view, FirstView. You can change the name of the template that Xamarin Studio gives us or just add a new one, but remember that the order of the files matters.

 

In the FirstView we have the following code:

 

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(0.0f,0.0f,320.0f,50.f)

        this.Add(label)

        textBox.Frame <- new RectangleF(0.0f,70.0f,320.0f,50.f)

        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

 


 

As you can see, it's just a view with two controls: one UILabel and one UITextField. In the ViewDidLoad method we initialize some properties of the controls and we also make the bindings:

 

CreateBindingSet is an extension method. An extension method is just a static method that can be called from the class that implements it, we just need to pass the instance of the object that will use it as an explicit parameter. When we called the Bind method, we have an analogy with the RaisePropertyChanged. Again I choose the overload that recieves a string so we don't have to do explicit casting.

 


 

Conclusion

 

This was the first contact, I personally, still need to think if using FSharp as a main language everywhere compensates, because currently we don't have the same tooling support than CSharp has. Perhaps it makes more sense to keep the view part in the CSharp side (in the Windows targets case). But for sure FSharp has some advantages over CSharp and also some disadvantages (tooling and communication between libraries) but as you know, the key is to "choose the right tool for the right job".

 

 

PS Sorry for the bad formatting of the code. I'll fixe that soon. 

You can also download it on git