Visual Studio solutions and projects for MAUI Blazor hybrid apps can be structured to allow added flexibility, fast feedback, and cost control in CI pipelines and workflows. The approach described is particularly relevant to MAUI Blazor hybrid apps. Especially where the majority of the application code is platform independent. And where the UI is mostly implemented in Blazor.
MAUI apps are awesome, but slow to build
MAUI Blazor hybrid apps are awesome. It’s easy to target multiple platforms (Android, iOS, Mac, and Windows) using MAUI. Blazor gives you a highly productive framework for developing the UI across those target platforms. And we get to leverage the goodness of C# and .NET. But MAUI apps are slow to build. Each target platform requires a separate build. And each of those builds take time and incur costs. For example, in GitHub Actions, minutes for Windows runners are 2x more than for Linux. Mac runners are 10x more. See here.
Typical MAUI app CI workflow is too slow
In a typical MAUI app CI workflow we might go through the following steps for each target platform.
- Build AUI app for specific platform
- Test built app
The long build time for each platform means that we have to wait a long time before we know if our build was successful and our tests all passed. It also means that each run of the CI workflow is relatively expensive. If we decide that building for each target platform on every push is too expensive. Then we need to look for more flexibility our workflows.
If all of the code is in the MAUI app project, then we don’t have much flexibility in how we build and test. What we want is to be able to build and test the more of our application code quickly and economically and get faster feedback in our CI workflow. We also want the freedom to make choices about when and how often the expensive platform-specific MAUI application builds are performed.
Move code out of the MAUI app project
Changing the Visual Studio solution and project structure can provide better CI workflow flexibility and cost control. The trick is to move the bulk of the code out of the MAUI app project into separate library projects.
Here’s an example Visual Studio solution structure.
- Visual Studio solution
- MAUI app
- MAUI app tests
- Blazor UI (Razor class library)
- Blazor UI tests
- Class library 1
- Class library 1 tests
- Class library 2
- Class library 2 tests
The bulk of the code, including the Blazor UI code, should be moved out of the MAUI Blazor hybrid app project. And into separate class library projects. A solution structure like this will allow us to build and tests the library projects separately. Without building the MAUI app project.
In a later blog post I’ll describe the steps for moving Blazor UI code out of a MAUI Blazor hybrid app project into a separate Razor class library project.
Using the flexibility in our workflows
With the new solution structure in place, we can trigger our CI workflow to build and test of our class library projects when code changes are pushed. Giving us quick and less expensive feedback.
We can decide when and how often the time-intensive and relatively expensive platform-specific MAUI application workflows are performed. For example, we may only want to trigger build and test of the MAUI app for each platform after a pull request has been approved. Or we may only want to trigger the workflow for the MAUI app for a specific platform after a particular process gate has been cleared for that platform.
We now have lots of flexibility for how we adjust our MAUI Blazor hybrid app workflows to meet our business and development needs.
Workflow for building and testing the libraries
To take advantage or that flexibility we need an easy to maintain technique that we can use in our CI workflows to build and test the libraries in the solution without building the MAUI app.
Building the Visual Studio solution using
dotnet build or
dotnet test commands will build all of the projects in the solution, including the MAUI app. So we need a technique that that won’t build include the MAUI app.
We could create a separate VS solution that does not include the MAUI app project. Or we could setup our workflow to specify the individual projects to build and test. But techniques like those require extra effort to maintain when projects are added or removed as the solution evolves. For example, if we forget to update when a project is added, then we probably won’t get any notice to remind us, and new tests might be skipped. We need a technique that is less error prone.
How about having the workflow remove the MAUI app project from the solution? In the library build and test workflow we can remove the MAUI app project from the solution so that it will be ignored. This technique works well and it doesn’t affect the separate workflows that build and publish the MAUI app for each platform (because those must always specify the specific project anyway).
Here’s an example of a job in a GitHub workflow for building and testing all of the class library projects in the solution. And it does it by removing the MAUI app from the solution.
I described how we can gain flexibility, fast feedback, and better cost control in our CI pipelines. By changing how our Visual Studio solution and project are organized. That restructuring includes splitting out projects for code that is time consuming or expensive to build. I also described a simple and easy to maintain technique for build and test workflows. So that specified projects in a solution can be excluded from by a pipeline. This post focused on MAUI Blazor hybrid apps. However, the concepts can be applied to Visual Studio solutions for any type of applications.