Modern UI frameworks such as Flutter and SwiftUI offer hot reload for declarative UI, in a single programming language.
By using CSharpForMarkup with LiveSharp, you can enjoy a similar developer experience in declarative C# for Xamarin Forms today.
See it in action:
Imo it is important for the future of Xamarin Forms that Microsoft supports and promotes this type of developer experience.
What is CSharpForMarkup?
CSharpForMarkup is a set of simple extension methods that lets you use declarative style C# for Xamarin Forms UI. It combines well with both the MVVM pattern (especially with Fody/PropertyChanged) and Reactive patterns, as well as with hot reload tools like LiveSharp and Continuous.
CSharpForMarkup offers similar or better readability than XAML, a better developer experience, and a lower learning curve + cognitive load (because you don’t need language bridging mechanisms like converters). You can choose stay close to XAML markup or you can add your specialized helpers to create a (company / team / app) specific markup DSL in C#. Both approaches are illustrated in this example in David Ortinau’s Xappy:
For detailed pro’s and con’s of declarative C# versus XAML, see the CSharpForMarkup readme on GitHub.
What is LiveSharp?
LiveSharp is a Visual Studio extension + NuGet package that lets you develop your C# code without recompilation. Unlike other hot reload tools, LiveSharp lets you modify any code on the fly (not just UI), while preserving the state of all objects in your app. While in a debugging session you edit your source files, and when you save a file the changes in that file instantly appear in your app. LiveSharp even supports simultaneous updates of your app running on multiple iOS and/or Android devices or simulators!
To get up and running quickly, see Getting Started with LiveSharp below.
Note that at this moment LiveSharp only works for Visual Studio on Windows. Support for other IDE’s is under development, including Visual Studio for Mac. To get an impression of hot-reloading CSharpForMarkup in VS for Mac, check out how Ryan Davis is doing that with Continuous. Ryan is using a private build of Continuous though; I haven’t heard of plans to make that a public supported product.
What Sets LiveSharp Apart
Most hot reload tools for Xamarin Forms, including XAML Hot Reload which Microsoft announced last week, only support XAML. This limits their value to a subset of the developers, app implementation and platforms. Some tools do support hot reloading C#, but they do so in a fundamentally limited way compared to LiveSharp.
“Other C# hot-reload tools typically use either Roslyn or the Mono interpreter to run the updated code in place.
LiveSharp is a hand-written interpreter that uses Expression Tree to compile new method bodies, which are then injected in statically compiled methods. This means that you don’t necessarily need to “reload” your page, object or application to see the modified behavior. For example, your app calls method “A” in a loop. With existing hot-reload solutions you would need to replace the whole object containing “A” to see updates. With LiveSharp you only update one method” (VH: LiveSharp will then invoke that method on all existing instances of the class, e.g. list items). “In a lot of cases it’s impossible for other tools to “reload” an object, for example some service that already has references to it from different parts of an app” (VH: Or e.g. page instances that are already somewhere in the navigation stack or that are cached for reuse).
You don’t have to replace the object instance with LiveSharp, although it might be beneficial with MVVM since there is usually a lot of logic in the constructor. That’s why the Xamarin Forms update handler actually constructs new view models and replaces the existing ones.”
“The second point is once Expression Tree is compiled it’s actually pretty close to statically compiled code performance wise.”
“The third point is that with LiveSharp you can theoretically develop the whole application without ever leaving the debugging session. This is due to the full control LiveSharp has over the interpretation process. While Roslyn and the Mono interpreter are general tools, LiveSharp is built specifically for the purpose of live coding.”
“Finally, an important point is that LiveSharp doesn’t depend on any 3rd party libraries and just needs a .NET Standard 2.0 target. Meaning it works in any project type that supports .netstandard20; Console/WinForms/WPF/XF/ASP.NET etc”
In short, LiveSharp lets you update more of your app code, better preserves app state, has better performance and supports more app types & platforms.
Note that LiveSharp does have some limitations, e.g. support for reflection and for assembly weavers like Fody/PropertyChanged on edited code (although Mihhail is looking into simulating Fody/PropertyChanged).
A release that supports most C# syntax and works well with Xamarin Forms pages, viewmodels and bindings is imminent. However, more is being worked on:
- LiveXAML integrated into LiveSharp
This lets you mix and match XAML + C# in your app any way you like, hot reloading both.
- Support Visual Studio for Windows, Visual Studio for Mac and Rider
Getting started with LiveSharp
To get started with LiveSharp today:
- Install the LiveSharp extension from the Visual Studio Marketplace in Visual Studio for Windows
- After starting Visual Studio, Windows Defender may present a prompt to allow LiveSharp Server communication. I want LiveSharp to work anywhere so I usually allow both private and public networks.
- A console window will open that shows LiveSharp Server logging:
If you see a connection error message here, you probably have a port conflict with another program. To solve this, close other programs and then close and reopen Visual Studio. After LiveSharp Server has connected successfully like shown above, you can safely open the conflicting applications again (I experienced this with AnyDesk and Skype for Business audio call).
- Install the LiveSharp NuGet in the project(s) you want hotreload to work in. I.e. a class library if you use that for your Forms code, or the Android and iOS projects if you use a Shared Project.
- After you build, a livesharp.rules file is added to the project(s):
The default livesharp.rules file “*” Injects update logic in all methods of all classes. That can quickly cause updates to fail because some methods may contain C# syntax that is not yet supported by LiveSharp. Typically I limit update to just methods whose name starts with Build on types in my Pages namespace:
For full documentation of the livesharp.rules file, see the LiveSharp GitHub repo readme.
- If you get a build warning like this:
You need to add a Start rule to the livesharp.rules file. This is where the LiveSharp client inside your app initializes itself at the start of a debugging session. LiveSharp may not be able to detect the starting point for your app automatically. The start rule can be any type of method; it does not have to be a constructor.A start rule for a default Xamarin Forms app constructor method without parameters looks like this:
You can also have constructors with parameters as a starting point. E.g. I specify an app constructor that has parameters, including one nested class type parameter, like this:
If the build fails and you get a build warning like: could not find file LiveSharp.Runtime.dll in <folder>, close and reopen VS and build again. It is a known issue that occurs in some solution structures.
- To enable editing C# while debugging, disable Edit and Continue in Visual Studio:
Makes perfect sense, right?
- Add a bare C# page to your app plus a way to navigate to it. To use CSharpForMarkup, simply add this XamarinFormsMarkupExtensions.cs file.You can separate the page markup from the page logic with partial class files, e.g.:
- Start debugging your app, navigate to the page, and type away! On each save the modified methods are sent to the app and invoked on all existing instances of the class.
In the VS output window and the LiveSharpServer console window you will see the update, including method names:
Note that you don’t have to be on the page that you are updating; the updated methods will be invoked on all existing and newly created instances of the class.
Also note that with the LiveSharp version used for this post, the first update may result in duplicating your controls in the existing page. To clean that up, navigate away from the page and back to it; after that you are good.
- The result may look like this:
Note that you can start the app on multiple devices and/or emulators manually. Even though you will only debug one app instance, all of them will update on save.
- Using LiveSharp in a debug build is fine, but you don’t want it included in a release build. Fortunately LiveSharp makes this easy; just include the LIVESHARP_DISABLE conditional compilation symbol and your build will contain nothing of LiveSharp:
The LiveSharp architecture is very flexible and even allows developers to customize the update logic. Maybe I’ll write a post on that later. This one is way too long as it is!
It is not a coincidence that LiveSharp and CSharpForMarkup go so well together; both originated from this discussion I started in March 2018 on the Xamarin forums and Twitter:
Mihhail started working on what would become LiveSharp, and I shared my helpers as CSharpForMarkup:
Ever since then I worked with Mihhail (who btw is a great guy and very smart dev), testing LiveSharp by building my production apps with it and occasionally advising on functionality. The goal is to make LiveSharp the best possible tool for hot-reloading C#.
Also, David Ortinau has been very supportive throughout this development. As Senior Program Manager, Mobile Dev Tools, Xamarin Mobile SDKs at Microsoft he had an inquisitive and open mindset from the start:
… right up to his “Embrace the best of all worlds” shout out at last week’s Xamarin Developer Summit:
Although LiveSharp is still a bit rough around the edges (it still is beta after all), it makes for a great developer experience when building UI in C# for Xamarin Forms. Especially when you combine it with CSharpForMarkup. I created production apps with it and would not hesitate to recommend it for real-world app development. LiveSharp has the potential to hot-reload all of your app code, not just UI. This combination takes developer productivity – and happiness – to a whole new level.
If you experience issues, please don’t hesitate to report them in CSharpForMarkup Issues or LiveSharp Issues.
Also feel free to share any questions, ideas, experiences and tips on CSharpForMarkup Gitter and LiveSharp Gitter.
Thanks and NJoy!