DEV Community

Cover image for LiveData: Bringing the best of Android to .NET
marplex
marplex

Posted on • Edited on

LiveData: Bringing the best of Android to .NET

I’ve been building Android apps for years; the development experience and community built around it is fantastic. There are a lot of open source libraries and projects from where you can learn from. Thanks to Android Jetpack and Google pushing MVVM design pattern adoption, almost every app follows the same rules and uses the same robust core libraries.
Microsoft vs Google

I can’t say the same for .NET and Microsoft. When I started working on WPF apps, I’ve immediately felt “uncomfortable”. There is a smaller community and really few open source projects, Microsoft is suggesting to use MVVM but you often need to break this pattern because there are controls and classes that are not built for it.

For years, Microsoft and it's closed-source philosophy slowed down the evolution of the .NET ecosystem. Now, after shifting towards a more "open" approach, Microsoft is trying to build back a strong developer community built around C# and .NET framework. They're making more open source libraries ("CommunityToolkit" clearly explains the new strategy) and extending support for other platforms such as Linux. Nevertheless, Microsoft is still years behind the Android developer experience.

Notify in the multiverse of madness

Doctor Strange notifying every property

When I started to develop WPF apps and write my first view model, I encountered what I call the "Notify madness" problem. Let me explain this better, have a look at this view model:

public class ViewModel : ObservableObject {

  private string name = "Marco";
  public string Name
  {
    get => name;
    set => SetProperty(ref name, value);
  }

}
Enter fullscreen mode Exit fullscreen mode

I find it absurd that you need to write 6 lines of code just to define a single notifiable property. Less than a year ago, Microsoft released MvvmToolkit 7.1 Preview where they finally introduced source generators. Now it’s way better, you can just add an attribute to a property and all that code is generated for you.

public partial class ViewModel : ObservableObject {

  [ObservableProperty]
  private string name;

}
Enter fullscreen mode Exit fullscreen mode

That being said, this feature was added just less than a year ago, when Android already had LiveData, Kotlin Flow and code generators were used for a long time to reduce boilerplate code.

Another aspect that I didn’t like about building view models was mapped properties. Every time the source property changed, you had to remember to notify all the other properties that were depending on it. With the latest MvvmToolkit releases you can use codegen with [AlsoNotifyChangeFor] to notify other properties automatically. But mapped properties should update by themselves. I don’t want to always add (and always forget) methods to notify the new values.

That’s why I’ve taken inspiration from Android LiveData and built a similar library for .NET. And because I always find creative names, I’ve called it…

LiveData library cover image

Bringing the best of Android to .NET

I decided to develop LiveData to simplify normal and mapped properties while having support for async operations. Anyway:

Talk is cheap. Show me the code.

public class LiveDataViewModel {

    public LiveData<string> Name => new("Marco");
    public LiveData<string> HelloMessage { get; }

    public LiveDataViewModel() {
        HelloMessage = Name.Map(x => $"Hello {x}!");
    }

}
Enter fullscreen mode Exit fullscreen mode

As you can see, every property is defined in one single line and the internal Value is automatically notified when changed. Mapped properties are also notified automatically, so there is literally no way to write unfinished or broken UIs just because you forgot to call notify().

Most of the time you will find yourself dealing with async tasks (like if you’re using Refit). LiveData automatically transforms async functions into bindable properties. For example:

//Map a string "SearchQuery" into an asynchronously retrieved list of users
Users = SearchQuery.MapAsync(query => api.SearchUsers(query));

//Convert an async function to a LiveData
LiveData<bool> IsVisible = asyncTask.ToLiveData<bool>();
Enter fullscreen mode Exit fullscreen mode

In a single line, async functions can be mapped and transformed to LiveData objects while automatically updating the UI. And finally, you can concatenate all this transformations to create complex reactive properties in just a few lines of code

//Concatenate transformation functions
FinalLiveData = Name.Delay(1000)
                    .Debounce(800)
                    .Map(name => "Hello" + name);
Enter fullscreen mode Exit fullscreen mode

To recap, here are all the advantages of using LiveData:

  • One line notifiable properties
  • You don’t have to remember to notify after every change
  • Mapped properties are notified automatically
  • Seamless async Task support
  • Easily create complex reactive properties

What’s next for LiveData

Although I’ve used LiveData in multiple projects, there are still a lot of improvements and new features to implement. Here’s a list of things I want to add:

  • Better lifecycle management
  • Better exception support
  • Personalized thread pools on async functions

Links

LiveData is free, open source and licensed under MIT. Contributions are welcomed 🥳

GitHub: https://github.com/Marplex/LiveData
NuGet: https://www.nuget.org/packages/LiveData/

Top comments (0)