Introducing TinyMvvm 2.0

TinyMvvm is a library that I created a couple of years ago. The idea was to build a small (tiny) MVVM library that made me more productive. I first released TinyNavigationHelper that helped med to abstract the Xamarin.Forms navigation, because I do not want my ViewModels to have references to Xamarin.Forms.

Last time I updated TinyMvvm was about one and a half year ago, the reason I have not updated it since then is that it has been stable and worked very well for me in multiple apps I have worked with for multiple clients.

When Xamarin.Forms Shell was released I started to work with some improvements to make TinyMvvm work with Xamarin.Forms Shell. But I never finished them, because the current version worked very well together with Shell. The only thing not supported was URL navigation.

But in the current app, I work with I had a use case where URL navigation would be very nice to use. Of course, I could have used it without using the navigation in TinyMvmm. But there are some great features of TinyMvvm that I missed then. For example to get navigation parameters in the ViewModel without having to pass them manually from the view and if I wanted to navigate from the ViewModel I had to add references to Xamarin.Forms inside of them.

New features

There is a couple of new stuff added to TinyMvvm in this release.

ShellNavigationHelper

If you want to use URL navigation together with TinyMvvm you have to use the ShellNavigtionHelper. ShellNavigationHelper is a subclass of FormsNavigationHelper.

var navigationHelper = new TinyNavigationHelper.Forms.ShellNavigationHelper();
navigationHelper.RegisterViewsInAssembly(appAssembly);
navigationHelper.RegisterView("MainView", typeof(MainView));

The method that is supporting URL navigation is the NavigateTo(string key) method. All other methods of INavigationHelper will use traditional Xamarin.Forms NavigationService. To NavigateTo you can either specify a URL or a key to a view. If it got a key that you have registered as in the code about it will use the traditional NavigationService, otherwise, it will use Shell navigation with URL.

await NavigationHelper.Current.NavigateTo("//home/messages?id=1");

If you are in a ViewModel with ViewModelBase as parent you can use the Navigation property of of the ViewModelBase,

await Navigation.NavigateTo("//home/messages?id=1");

QueryParameters in ViewModelBase

With URL navigation you can pass query parameters. With TinyMvvm you can access them from the ViewModel via the QueryParameters property of ViewModelBase. QueryParameters is of type Dictionary<string, string>.

public override async Task Initialize()
{
    await base.Initialize();

    var id = QueryParameters["id"];
}

Optimization

In TinyMvvm 1.x there was a dependency on TinyNavigationHelper, in 2.0 is TinyNavigationHelper added as a submodule and compiled into the same assembly as TinyMvvm. This reduces the amount of referenced assemblies that Xamarin.Forms have to search for custom renderers in.

Breaking changes

The constructor to FormsNavigationHelper has changed. You can not pass an instance of Xamarin.Forms.Application anymore, the reason is that it not is necessary to provide the navigation helper with it.

Obsolete fetures

The BeginInvokeOnMainThread future of ViewModelBase has been marked as obsolete, the recommendation is to use Xamarin.Essentials.MainThread.BeginInvokeOnMainThread instead.

TinyMvvm basics

Here are some of the basics of TinyMvvm, you will find the full docs and a sample project here, https://github.com/TinyStuff/TinyMvvm.

Initiation

TinyMvvm.Forms.TinyMvvm.Initialize();

ViewBase<T>

Features (or drawbacks) of the ViewBase

  • It creates the ViewModel for you if you inherit the ViewModel from ViewModelBase

The view should inherit from ViewBase<T> where T is the ViewModel. The ViewModel can be any class that has ViewModelBase as the base class.

ViewBase<T> itself inherits from Xamarin.Forms.ContentPage and can be treated by Xamarin Forms as any page.

If you decide to use ViewModelBase as the base class for your view model and at the same time have the IoC resolver enabled, the view will automatically create the view model for you when the view is created. Hence no need to inject the view model in the constructor and assign it manually. Feature or not, you decide.

An example of a typical page in TinyMvvm would look like this:

<tinymvvm:ViewBase x:TypeArguments="viewmodels:MainViewModel" 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:viewmodels="clr-namespace:TinyMvvm.Forms.Sample.ViewModels;assembly=TinyMvvm.Forms.Samples"
    xmlns:tinymvvm="clr-namespace:TinyMvvm.Forms;assembly=TinyMvvm.Forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    x:Class="TinyMvvm.Forms.Sample.Views.MainView">

</tinymvvm:ViewBase>

What you need to do is:

  • Define two namespaces (viewmodels and tinymvvm)
  • Change the view base type to tinymvvm:ViewBase
  • Add a type argument pointing to your ViewModel

### ViewModelBase

TinyMvvm also defines a base class for the view model called `ViewModelBase`.

Features of the ViewModelBase

* Wraps navigation for you through the INavigation interface (Implemented in TinyNavigation)
* Implements INotifyPropertyChanged for you
* Propagates life cycle events to the view (Initialize, OnAppearing, OnDisapparing)

### IoC
Tiny Mvvm is not bound to any specific IoC provider. There is a provider for Autofac that you can install with the "TinyMvvm.Autofac" package.

Install-Package TinyMvvm.Autofac

TinyMvvm has a Resolver in its core project. To use it you need to add on a provider to it that implements the IResolver interface, for example, our Autofac provider.

```csharp
var container = builder.Build();
var resolver = new AutofacResolver(container);
Resolver.SetResolver(resolver);
var navigationHelper = Resolver.Resolve();

TinyCommand

TinyMvvm has its own implementation of ICommand that not has any references to Xamarin.Forms so you can use it in a library without reference Xamarin.Forms.

public ICommand WithParameter
{
      get
      {
                return new TinyCommand(async() =>
                {
                   //Do stuff
                });
      }
}

Introducing TinySvgHelper

For a pretty long time I have had code that I have used for a couple of different apps that uses SkiaSharp to convert an svg image to a Xamarin.Forms ImageSource. I also blogged about it two years ago, https://danielhindrikes.se/index.php/2018/03/13/use-skiasharp-to-convert-svg-to-xamarin-forms-imagesource/. But I never released it as a library until now.

Get started with TinySvgHelper

The library is published to NuGet, https://www.nuget.org/packages/TinySvgHelper/

To install it, search for TinySvgHelper in the **Nuget Package Manager** or install it with the following command:

Install-Package TinySvgHelper

To use it you must add SvgHelper.Init() to your MainActivity and/or AppDelegate.

MainActivity

protected override void OnCreate(Bundle savedInstanceState)
{
    ....
 
    SvgHelper.Init();
 
    ...
}

AppDelegate

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    ...
 
    SvgHelper.Init();
 
    ...
}

Use TinySvgHelper

You can use TinySvgHelper either from your XAML- or C# code. For iOS add the svg files to the **Resources** folder and for Android add the svg files to the **Assets** folder.

XAML

First you need to import the namespace:

xmlns:svg="clr-namespace:TinySvgHelper;assembly=TinySvgHelper"

Then you will use it as a markup extension:

<Image Source="{svg:Svg FileName='logo.svg', Height=200,Width=200}" />

You can also specify a color to it:

<ShellContent Icon="{svg:Svg FileName='info.svg', Height=22,Width=22,Color=Black}" Title="Start">
    <views:StartView />
</ShellContent>
```
 
### C#
<pre lang="csharp">
using TinySvgHelper;
var image = new Image();
image.Source = SvgHelper.GetAsImageSource("info.svg", 50, 50, Color.Blue);

Read more

You can find the full documentation, the code and a sample project on GitHub, https://github.com/TinyStuff/TinySvgHelper

TinyCache 2.0

TinyCache, https://github.com/TinyStuff/TinyCache, is an open-source library created by Mats Törnberg and it is a part of the collection of libraries that we call TinyStuff. It is a library that makes caching a little bit easier.

Today we are releasing version 2.0 of TinyCache. In the first version, you could only have a single instance of TinyCahce. But I made some changes and added a pull request to the library that makes it possible to have multiple instances. The reason for that is that sometimes I wanted to have different settings for the cache in different situations in the same app. If you have used TinyCache before, an update to 2.0 will break your code. This blog post will introduce you to the new changes, but also to how TinyCache is working if you don't have used it before.

TinyCacheHandler

TinyCache 2.0 introduces TinyCacheHandler that will handle the instances of TinyCache. To make this possible, TinyCache and its method is no longer static.

If we only want one instance of TinyCache, we can use the Default method of TinyCacheHandler. The first time you access the Default property a new instance of TinyCache will be created if there are no instances created. This instance will get default as the key.

Create an additional instance of TinyCache

To add a new instance of TinyCacheHandler we can use either the create method or the add method.

Using the Create method:

var newCache = TinyCacheHandler.Create("myNewCache");

Using the Add method:

var newCache = new TinyCache();
TinyCacheHandler.Add("myNewCache", newCache);

Set default cache

If we have multiple cache instances, we may not want the first one to be the default, then we can change that bypassing the cache key to the SetDefault method of TinyCacheHandler.

TinyCacheHandler.SetDefault("myNewCache");

Get a specific instance of TinyCache

If we want to get an instance of TinyCache that not are the default instance, we can use the key for the instance as in the code below.

var cache = TinyCacheHandler.Get("myNewCache");
 
var result = cache.RunAsync<List<Data>>("myNewCache", () => { return api.GetData("customdata"); });

Use TinyCache

To use TinyCache, just use the RunAsync method and pass the cache key and how to fetch the data. If there are data in the cache, it will return cached data.

// Fetch data with default policy
var result = await TinyCacheHandler.Default.RunAsync<List<Data>>("cachekey", () => { return api.GetData("customdata"); });

TinyCache policies

With policies you can set how the cache should work, when should it refresh data etc.

Here is an example how we can use policies:

TinyCacheHandler.Default.SetBasePolicy(
    new TinyCachePolicy()
        .SetMode(TinyCacheModeEnum.CacheFirst) // fetch from cache first
        .SetFetchTimeout(TimeSpan.FromSeconds(5)) // 5 second excecution limit
        .SetExpirationTime(TimeSpan.FromMinutes(10)) // 10 minute expiration before next fetch
        .SetUpdateCacheTimeout(50) // Wait 50ms before trying to update cache in background

TinyCache stores

If we use TinyCache without setting a store, it will only cache data in the memory. But if we want to persist the data there are a couple of options. I mostly use the file storage store.

TinyCache.FileStorage
Is provided by the NuGet package, TinyCache.FileStorage. This store is persisting the cache data to a file at the filesystem of the device.

var store = new FileStorage();
 
var cacheFolder = string.Empty;
 
#if __IOS__ || __MACOS__
            cacheFolder = NSSearchPath.GetDirectories(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomain.User)[0];
#elif __ANDROID__
            cacheFolder = Application.Context.CacheDir.AbsolutePath;
#elif __UWP__
            cacheFolder = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
#else
            cacheFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
#endif
 
store.Initialize(cacheFolder);
 
// Set cache storage
TinyCacheHandler.Default.SetCacheStore(store);

TinyCache.Forms
Is provided by the NuGet package, TinyCache.Forms. This store is using the property storage that is provided by the Application class in Xamarin.Forms

var store = new XamarinPropertyStorage();
 
// Set cache storage
TinyCacheHandler.Default.SetCacheStore(store);

Read more

You will find both the documentation and the source code in the GitHub repository, https://github.com/TinyStuff/TinyCache.

TinyNavigationHelper for Xamarin.Forms

TinyNavigationHelper is a helper that I created together with Johan Karlsson for a project where I don't want to install a separate MVVM framework, but I still wanted to have an abstraction for the Navigation so that I could handle navigation in my ViewModels without having a reference to Xamarin.Forms in the ViewModels. The reason for that is that I want to have my ViewModels clean from platform specific libraries so I can use them for other platforms as well, for example, if I want to extend my application with support for WPF.

You will find the source code on GitHub and you're more than welcome to contribute and give feedback.
https://github.com/dhindrik/TinyNavigationHelper. You will also find the complete documentation on GitHub.

Installation
The easiest way to install it is via NuGet:
Install-Package TinyNavigationHelper.Forms

And for projects that not reference Xamarin.Forms:
Install-Package TinyNavigationHelper.Abstraction

Configure TinyNavigationHelper
There are three ways to configure the navigation, the two first is to register the type of the view together with a key that you define. The third way is to register all views in an assembly. What the method will do that it searching throw the assembly for classes that inherit from Page.

// Option 1: Register single views
var navigationHelper = new FormsNavigationHelper(this);
navigationHelper.RegisterView("MainView");
 
// Option 2: Register single views
var navigationHelper = new FormsNavigationHelper(this);
navigationHelper.RegisterView("MainView", typeof(MainView));
 
// Option 3: Register all views (pages) that is inherited from Page
// The class name will be the key. To use this, you need to add using System.Reflection;
var asm = typeof(App).GetTypeInfo().Assembly;
navigationHelper.RegisterViewsInAssembly(asm);

If you want to use it with dependency injection you can register FormsNavigationHelper with the INavigationHelper interface.

Use TinyNavigationHelper
You can always get the current instance of TinyNavigationHelper via the static Current property on from the NavigationHelper class.

var navigationHelper = NavigationHelper.Current;

To navigate to a view, use the NavigateToAsync method.

await navigationHelper.NavigateToAsync("MainView");
 
//With parameter
await navigationHelper.NavigateToAsync("MainView", "Parameter");

The parameter will be sent to the constructor of the view.

public class MainView
{
     public MainView(object parameter)
     {
          var data = parameter as string;
     }
}

To go back, use the BackAsync method.

await navigationHelper.BackAsync();

You can also open a modal with the OpenModalAsync method.

await navigationHelper.OpenModalAsync("MainView");
 
//open a modal with an own navigation page
await navigationHelper.OpenModalAsync("MainView", true);
 
//with parameter
await navigationHelper.OpenModalAsync("MainView", "parameter", true);

You will close the modal with the CloseModalAsync method.

await navigationHelper.CloseModalAsync();

If you want to reset the navgation stack, you can use the SetRootView method.

//without parameter and navigation stack
navigationHelper.SetRootView("MainView");
 
//with parameter, but without navigation stack
navigationHelper.SetRootView("MainView", "parameter");
 
//without parameter, but with navigation stack
navigationHelper.SetRootView("MainView", true);
 
//with parameter and navigation stack
navigationHelper.SetRootView("MainView", "parameter", true);