<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Thomas M</title>
    <description>The latest articles on Forem by Thomas M (@tmcd054).</description>
    <link>https://forem.com/tmcd054</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2197334%2F9eccd9b6-b5ab-4335-828c-7cd850e598eb.jpeg</url>
      <title>Forem: Thomas M</title>
      <link>https://forem.com/tmcd054</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tmcd054"/>
    <language>en</language>
    <item>
      <title>Turning .NET MAUI Upgrades into a Repeatable, Low‑Risk Process</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Mon, 26 Jan 2026 13:55:17 +0000</pubDate>
      <link>https://forem.com/tmcd054/turning-net-maui-upgrades-into-a-repeatable-low-risk-process-1ci9</link>
      <guid>https://forem.com/tmcd054/turning-net-maui-upgrades-into-a-repeatable-low-risk-process-1ci9</guid>
      <description>&lt;p&gt;Upgrading a .NET MAUI application is rarely hard because of one breaking change.&lt;br&gt;
It’s hard because upgrades sit at the intersection of framework evolution, platform requirements, tooling, and existing architecture — and those things rarely move in sync.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;“simple”&lt;/strong&gt; MAUI upgrade can involve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new .NET SDK&lt;/li&gt;
&lt;li&gt;Updated MAUI workloads&lt;/li&gt;
&lt;li&gt;Android and iOS minimum version changes&lt;/li&gt;
&lt;li&gt;Package deprecations&lt;/li&gt;
&lt;li&gt;Build pipeline assumptions that no longer hold&lt;/li&gt;
&lt;li&gt;Platform issues that only appear once the app actually launches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most teams know this — yet upgrades are still handled as ad‑hoc tasks, driven by release pressure or looming support deadlines. That’s usually where things start to go wrong.&lt;br&gt;
I wanted to change that.&lt;/p&gt;

&lt;p&gt;Instead of treating MAUI upgrades as a one‑off activity, I treated them as a design problem — and the result was a reusable upgrade prompt whose job is to enforce good engineering behaviour by default.&lt;/p&gt;

&lt;p&gt;This post explains why that prompt exists, how it’s structured, and why the prompt itself is the real product.&lt;/p&gt;
&lt;h2&gt;
  
  
  ⭐ The Prompt Is the Solution
&lt;/h2&gt;

&lt;p&gt;This approach isn’t about AI magically upgrading your app for you.&lt;br&gt;
The prompt is the star of the show.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧠 It encodes experience&lt;/li&gt;
&lt;li&gt;🛑 It enforces discipline&lt;/li&gt;
&lt;li&gt;🧭 It removes ambiguity&lt;/li&gt;
&lt;li&gt;📋 It turns upgrades into a guided, auditable workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without a strong prompt, assistants tend to optimise for “making things compile”.&lt;/p&gt;

&lt;p&gt;With a strong prompt, they optimise for doing the upgrade properly.&lt;br&gt;
The assistant becomes an execution partner — but the prompt defines the rules of engagement.&lt;/p&gt;
&lt;h2&gt;
  
  
  🚨 What the Prompt Is Designed to Prevent
&lt;/h2&gt;

&lt;p&gt;The prompt exists because the same failure patterns show up again and again in MAUI upgrades:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Accidentally upgrading to preview or RC SDKs&lt;/li&gt;
&lt;li&gt;❌ Architecture drift during “harmless” refactors&lt;/li&gt;
&lt;li&gt;❌ Dependency churn justified as “best practice”&lt;/li&gt;
&lt;li&gt;❌ Full solution builds run blindly and repeatedly&lt;/li&gt;
&lt;li&gt;❌ Android or iOS issues discovered at the very end&lt;/li&gt;
&lt;li&gt;❌ CI pipelines failing after the upgrade is merged&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren’t edge cases — they’re the default outcome of unstructured upgrades.&lt;/p&gt;

&lt;p&gt;Every section of the prompt exists to counter one of these failure modes.&lt;/p&gt;
&lt;h2&gt;
  
  
  🧩 A Practical MAUI Upgrade Prompt Pattern
&lt;/h2&gt;

&lt;p&gt;Below is a practical example of the upgrade prompt pattern I use.&lt;br&gt;
It focuses on structure, sequencing, and guardrails — the things that actually make upgrades predictable and repeatable — rather than on any single application or codebase.&lt;/p&gt;

&lt;p&gt;💡 Think of this as a blueprint, &lt;strong&gt;not&lt;/strong&gt; a script.&lt;/p&gt;

&lt;p&gt;The value is in how the pieces fit together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// MAUI Upgrade Prompt Pattern
// Purpose: enforce a structured, low-risk MAUI upgrade process

You are an experienced .NET MAUI engineer performing a framework upgrade.

Target the latest fully released, stable MAUI version unless otherwise specified.
Do NOT use preview, RC, or beta SDKs, workloads, or NuGet packages.

Primary objectives:
- Maintain application stability throughout the upgrade
- Preserve architectural intent and existing design decisions
- Minimise unnecessary or speculative changes
- Ensure Android and iOS builds succeed and launch

Architectural constraints:
- Preserve existing navigation patterns
- Maintain strict MVVM boundaries
- Keep views lightweight and UI-focused
- Avoid conditional compilation in shared source code
- Place platform-specific code behind proper abstractions

Before making any changes:
- Inventory current MAUI and .NET versions
- List all projects and target frameworks (TFMs)
- Identify key packages and build tooling
- Review CI/CD pipelines for SDK or MAUI assumptions
- Confirm required SDKs and workloads are installed

Upgrade workflow:
1. Plan the upgrade and document required changes
2. Update project files and target frameworks
3. Align dependencies using stable releases only
4. Address breaking changes deliberately and incrementally
5. Build and test incrementally, avoiding unnecessary rebuilds
6. Validate Android and iOS builds and app launch
7. Review and justify any optional or non-essential changes

Operational rules:
- Warn before running long or expensive build commands
- Pause after each major step for review or confirmation
- Produce a final summary of changes, outcomes, and risks

Definition of done:
- Solution builds successfully
- Android app launches successfully
- iOS app launches successfully
- Architectural intent remains intact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prompt is intentionally opinionated — because upgrades are &lt;strong&gt;not&lt;/strong&gt; the place for ambiguity.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Why Each Section Exists
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🎯 1. Role and Quality Bar
&lt;/h3&gt;

&lt;p&gt;The prompt starts by defining how the assistant must behave.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Stability‑first mindset&lt;/li&gt;
&lt;li&gt;✅ No experimentation during upgrades&lt;/li&gt;
&lt;li&gt;✅ Engineering judgement over novelty&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this, tools tend to suggest newer APIs, introduce architectural changes “for improvement”, or optimise for cleverness rather than safety.&lt;br&gt;
Upgrades demand restraint — the prompt enforces it.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏗️ 2. Architectural Guardrails
&lt;/h3&gt;

&lt;p&gt;Upgrades are where architecture erodes quietly.&lt;br&gt;
The prompt explicitly protects against that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No surprise navigation changes&lt;/li&gt;
&lt;li&gt;No UI logic leakage&lt;/li&gt;
&lt;li&gt;No platform hacks in shared code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is that the application still feels like the same system after the upgrade — just on a newer framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⏱️ 3. Build‑Time Awareness
&lt;/h3&gt;

&lt;p&gt;MAUI builds are expensive, slow, and easy to overuse.&lt;br&gt;
The prompt treats build time as a cost to manage, not an afterthought:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚠️ Warn before long builds&lt;/li&gt;
&lt;li&gt;⚡ Prefer incremental validation&lt;/li&gt;
&lt;li&gt;🧹 Avoid unnecessary cleans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps humans in control and prevents blind iteration.&lt;/p&gt;

&lt;h3&gt;
  
  
  🗺️ 4. Context Before Action
&lt;/h3&gt;

&lt;p&gt;Most upgrade pain comes from missing context.&lt;br&gt;
The prompt forces an inventory before touching code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Framework versions&lt;/li&gt;
&lt;li&gt;Project structure&lt;/li&gt;
&lt;li&gt;Dependencies&lt;/li&gt;
&lt;li&gt;CI/CD assumptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing changes until the landscape is understood.&lt;br&gt;
This alone eliminates a huge class of late‑stage surprises.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚦 5. Gated, Sequential Execution
&lt;/h3&gt;

&lt;p&gt;The upgrade is broken into explicit phases, each with pause points.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Failures are isolated&lt;/li&gt;
&lt;li&gt;✅ Reviews are meaningful&lt;/li&gt;
&lt;li&gt;✅ Rollback remains viable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This mirrors how experienced engineers already work — but makes it explicit and repeatable.&lt;/p&gt;

&lt;h3&gt;
  
  
  📱 6. Platform Validation Is Non‑Optional
&lt;/h3&gt;

&lt;p&gt;The prompt does not consider the upgrade complete unless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Android builds and launches&lt;/li&gt;
&lt;li&gt;✅ iOS builds and launches&lt;/li&gt;
&lt;li&gt;“It builds” is not a definition of done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mobile apps fail on devices, not in IDEs — and the prompt enforces that reality.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔍 7. Review of “Optional” Changes
&lt;/h3&gt;

&lt;p&gt;One subtle but powerful rule:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The prompt explicitly asks for a review of changes that might not be required.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why this matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevents silent scope creep&lt;/li&gt;
&lt;li&gt;Documents trade‑offs&lt;/li&gt;
&lt;li&gt;Keeps upgrades honest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where discipline usually slips — and where it’s restored.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Why This Works
&lt;/h2&gt;

&lt;p&gt;This approach doesn’t automate expertise — it captures it.&lt;br&gt;
The prompt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makes assumptions explicit&lt;/li&gt;
&lt;li&gt;Encodes hard‑won experience&lt;/li&gt;
&lt;li&gt;Reduces cognitive load&lt;/li&gt;
&lt;li&gt;Scales across teams and projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly, it turns MAUI upgrades from a stressful event into a repeatable engineering process.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;If your MAUI upgrades feel unpredictable, the problem usually isn’t the framework.&lt;br&gt;
It’s the lack of structure.&lt;br&gt;
A well‑designed prompt won’t remove the need for engineers —&lt;br&gt;
but it will ensure their experience shows up every single time.&lt;br&gt;
And once you’ve used this approach for upgrades, you’ll start seeing other places where a good prompt can act as guardrails — migrations, refactors, and CI/CD changes included.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>dotnet</category>
      <category>promptengineering</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Exploring Azure ADO MCP Server: A Smart Assistant for Smarter Development</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Wed, 14 Jan 2026 15:57:48 +0000</pubDate>
      <link>https://forem.com/tmcd054/exploring-azure-ado-mcp-server-a-smart-assistant-for-smarter-development-3p2e</link>
      <guid>https://forem.com/tmcd054/exploring-azure-ado-mcp-server-a-smart-assistant-for-smarter-development-3p2e</guid>
      <description>&lt;p&gt;Over the past few weeks, I’ve been experimenting with the Azure DevOps MCP (Model Context Protocol) server, and it’s quickly become a valuable part of my development workflow. I’m always on the lookout for tools that support quality assurance and help reduce scope creep — and MCP is shaping up to be exactly that.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 What Is ADO MCP?
&lt;/h2&gt;

&lt;p&gt;The Azure DevOps MCP server acts as an intelligent agent inside Azure DevOps, analysing code changes in the context of user stories, acceptance criteria, and broader project objectives. Instead of simply reviewing what was changed, it helps evaluate why those changes were made and whether they fully address the requirements.&lt;br&gt;
You can learn more about the Azure DevOps MCP Server setup via:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/microsoft/azure-devops-mcp" rel="noopener noreferrer"&gt;Azure Devops MCP Github Repo&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://devblogs.microsoft.com/devops/azure-devops-local-mcp-server-generally-available/" rel="noopener noreferrer"&gt;Microsoft's Official Blog Post on Azure DevOps MCP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 Use Case: Validating Scope Against Acceptance Criteria
&lt;/h2&gt;

&lt;p&gt;A recent example involved a task to add a new field to a user interface. Straightforward enough — but the related requirements also involved updating the underlying data records, a detail that’s easy to miss.&lt;br&gt;
Using MCP in agent mode, I asked it to compare my branch with the main branch and evaluate the changes against the story description and acceptance criteria. MCP correctly flagged that while the UI updates were complete, the associated data updates were missing.&lt;br&gt;
In this case, the omission was intentional — a small experiment to see how well MCP could detect the gap. It did exactly that.&lt;br&gt;
Even better, because I was using agent mode, MCP didn’t stop at identifying the issue. It proposed the necessary data updates, applied them, and confirmed that the acceptance criteria were now fully met.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Why This Matters
&lt;/h2&gt;

&lt;p&gt;This kind of contextual, requirement‑aware validation can significantly improve development workflows by offering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fewer missed requirements&lt;/li&gt;
&lt;li&gt;Faster feedback loops&lt;/li&gt;
&lt;li&gt;Higher confidence that work items are complete and aligned with their acceptance criteria&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As long as user stories are written with clear descriptions and robust acceptance criteria, MCP provides an additional layer of assurance that’s both practical and powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Tools like the MCP aren’t here to replace developers — they’re here to enhance the way we work. By introducing context‑aware reasoning into our development processes, we can deliver with greater confidence and catch issues that might otherwise slip through. I’ll definitely be keeping MCP in my toolkit going forward.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>azure</category>
      <category>devops</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Migrating to MAUI 10: What Architects Need to Know</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Wed, 14 Jan 2026 15:45:31 +0000</pubDate>
      <link>https://forem.com/tmcd054/migrating-to-maui-10-what-architects-need-to-know-5f4k</link>
      <guid>https://forem.com/tmcd054/migrating-to-maui-10-what-architects-need-to-know-5f4k</guid>
      <description>&lt;p&gt;If you’ve been working with .NET MAUI—or planning a migration from Xamarin—&lt;strong&gt;MAUI 10 is a release you shouldn’t ignore&lt;/strong&gt;. This isn’t just a routine update; it delivers meaningful improvements in performance, developer experience, and architectural clarity. Here’s what’s new, what’s changed, and how to plan your migration with confidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔥 Why Upgrade to MAUI 10?
&lt;/h2&gt;

&lt;p&gt;MAUI 10 introduces enhancements that matter for both developers and architects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance Improvements&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster startup, reduced memory usage, and improved Hot Reload make the development loop smoother and the end experience snappier.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Modern Development Patterns&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Async‑first APIs, simplified layouts, and clearer architectural boundaries reduce complexity and technical debt.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Enterprise‑Ready Capabilities&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built‑in telemetry hooks, better observability, and improved control over embedded web content.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧱 Key Architectural Changes
&lt;/h2&gt;

&lt;p&gt;Migrating isn’t just updating your NuGet packages — it’s understanding the underlying shifts.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Async‑First Animation APIs
&lt;/h2&gt;

&lt;p&gt;Legacy animation methods like &lt;code&gt;FadeTo&lt;/code&gt; and &lt;code&gt;RotateTo&lt;/code&gt; now have async equivalents (&lt;code&gt;FadeToAsync&lt;/code&gt;, &lt;code&gt;RotateToAsync&lt;/code&gt;). These follow modern async patterns and help you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sequence animations naturally
&lt;/li&gt;
&lt;li&gt;support cancellation
&lt;/li&gt;
&lt;li&gt;avoid UI thread blocking
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✔️ Cleaner MAUI 10 Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AnimateBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;AnimatedBox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FadeToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;AnimatedBox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RotateToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThrowIfCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AnimatedBox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FadeToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Predictable sequencing, easier chaining, better responsiveness.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🧹 Removal of Legacy Components
&lt;/h3&gt;

&lt;p&gt;Some legacy Xamarin‑compatibility APIs are now deprecated or removed. Here are the notable ones:&lt;/p&gt;

&lt;h4&gt;
  
  
  👆 Gesture Recognisers.
&lt;/h4&gt;

&lt;p&gt;ClickGestureRecognizer (from the old compatibility stack) has been removed.&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;TapGestureRecognizer&lt;/strong&gt; instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"logo.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Image.GestureRecognizers&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;TapGestureRecognizer&lt;/span&gt; 
            &lt;span class="na"&gt;NumberOfTapsRequired=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
            &lt;span class="na"&gt;Tapped=&lt;/span&gt;&lt;span class="s"&gt;"OnTapGestureRecognizerTapped"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Image.GestureRecognizers&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  📐 Layout Changes
&lt;/h4&gt;

&lt;p&gt;Compatibility layouts (e.g., older Forms-based ones) are deprecated and being phased out. Modern layouts offer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;better performance&lt;/li&gt;
&lt;li&gt;deterministic layout behaviour&lt;/li&gt;
&lt;li&gt;cleaner XAML&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommended layouts:&lt;br&gt;
Grid, StackLayout, FlexLayout, and VerticalStackLayout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;Padding=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt;
      &lt;span class="na"&gt;RowDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"Auto,*"&lt;/span&gt;
      &lt;span class="na"&gt;ColumnDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"Auto,*"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;Grid.Row=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; 
           &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"avatar.png"&lt;/span&gt; 
           &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt; &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Grid.Row=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; 
           &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Welcome"&lt;/span&gt; &lt;span class="na"&gt;FontAttributes=&lt;/span&gt;&lt;span class="s"&gt;"Bold"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;CollectionView&lt;/span&gt; &lt;span class="na"&gt;Grid.Row=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; 
                    &lt;span class="na"&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
                    &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Items}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Cleaner architecture with no legacy compatibility layers.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🌐 HybridWebView &amp;amp; WebView Interception Enhancements
&lt;/h3&gt;

&lt;p&gt;If your app embeds web content, MAUI 10 introduces meaningful improvements. You can now intercept and handle web requests through WebView handler APIs and—where supported—through HybridWebView or BlazorWebView integrations.&lt;br&gt;
Typical use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Injecting enterprise security headers&lt;/li&gt;
&lt;li&gt;Capturing and redirecting requests&lt;/li&gt;
&lt;li&gt;Overriding responses&lt;/li&gt;
&lt;li&gt;Handling secure API calls in hybrid apps&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Interception availability varies slightly by platform; always test behaviour on Android, iOS, and Windows.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Example: Intercepting Requests
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;InitializeWebView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;DemoWebView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebResourceRequested&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;OnWebViewWebResourceRequested&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;DemoWebView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawMessageReceived&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;OnWebViewRawMessageReceived&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnWebViewWebResourceRequested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebViewWebResourceRequestedEventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/secure"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;GetCustomStream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt; &lt;span class="nf"&gt;GetCustomStream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"{\"status\":\"success\",\"message\":\"Everything is fine!\",\"timestamp\":\""&lt;/span&gt;
        &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"o"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\"}"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnWebViewRawMessageReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HybridWebViewRawMessageReceivedEventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;MainThread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginInvokeOnMainThread&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;WebStatusLabel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Message from JS: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Better control, improved security, and richer hybrid scenarios.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧭 Migration Strategy for Architects
&lt;/h2&gt;

&lt;p&gt;Here’s the calm, structured approach to avoid surprises:&lt;/p&gt;

&lt;h3&gt;
  
  
  🔍 1. Audit Your Codebase
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Identify deprecated gesture recognizers&lt;/li&gt;
&lt;li&gt;Flag legacy layout usage&lt;/li&gt;
&lt;li&gt;Check animation logic for sync‑based APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚠️ 2. Prepare for Breaking Changes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Update animation code paths&lt;/li&gt;
&lt;li&gt;Replace obsolete compatibility components&lt;/li&gt;
&lt;li&gt;Test WebView‑based features across target platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🚀 3. Modernise During Migration
&lt;/h3&gt;

&lt;p&gt;Don’t just port — upgrade.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adopt async‑first UI flows&lt;/li&gt;
&lt;li&gt;Move to modern layouts&lt;/li&gt;
&lt;li&gt;Leverage WebView interception for enterprise scenarios&lt;/li&gt;
&lt;li&gt;Integrate richer telemetry hooks (MAUI + App Insights)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ❗ Common Pitfalls
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Treating the migration as a simple “lift and shift”&lt;/li&gt;
&lt;li&gt;Ignoring deprecated layout or gesture APIs&lt;/li&gt;
&lt;li&gt;Not testing hybrid features across all platforms&lt;/li&gt;
&lt;li&gt;Overlooking the opportunity to simplify UI architecture&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Upgrading to MAUI 10 isn’t just about staying current — it’s an opportunity to modernise your architecture, remove legacy baggage, and deliver faster, more maintainable apps.&lt;br&gt;
By embracing modern APIs, cleaning up deprecated components, and adopting updated design patterns, your migration becomes an investment in long‑term productivity and performance.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>dotnet</category>
      <category>mobile</category>
      <category>performance</category>
    </item>
    <item>
      <title>Exploring the Potential of Copilot Agents for Large-Scale Application Migrations</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Thu, 15 May 2025 13:21:44 +0000</pubDate>
      <link>https://forem.com/tmcd054/exploring-the-potential-of-copilot-agents-for-large-scale-application-migrations-5fl0</link>
      <guid>https://forem.com/tmcd054/exploring-the-potential-of-copilot-agents-for-large-scale-application-migrations-5fl0</guid>
      <description>&lt;p&gt;As a developer, migrating a large-scale web application can be a daunting task. The complexity and scale of such projects often require innovative solutions to streamline the process and ensure success. Recently, I embarked on this journey, aiming to harness the power of AI to facilitate the migration. My initial approach involved using Copilot Chat, a tool I frequently relied on for various tasks. However, I soon realised that while Copilot Chat was helpful, it struggled to fully comprehend the intricacies of my large application.&lt;/p&gt;

&lt;p&gt;This led me to explore other options, and that's when I discovered Copilot Agents. Unlike Copilot Chat, Copilot Agents seemed capable of understanding and processing larger applications, which was a promising development. The responsiveness of Copilot Agents to my prompts was particularly noteworthy. It felt like I was venturing into the realm of prompt engineering, where the agent could be guided more effectively based on the prompts I provided.&lt;/p&gt;

&lt;p&gt;One of the most intriguing aspects of using Copilot Agents was its ability to process my requests and respond with questions that built further actions. This dynamic interaction was almost like having a virtual pair programming session with myself. The agent's capacity to comprehend my needs and provide actionable insights made the migration process more manageable and less overwhelming.&lt;/p&gt;

&lt;p&gt;At this stage, I am still in the early phases of my migration journey. However, the potential of Copilot Agents as a tool for large-scale web application migration is evident. I am keen to see how this tool will continue to support my efforts and streamline the process. As I progress, I plan to share more insights and updates through additional blog posts, documenting my experiences and the effectiveness of Copilot Agents in this venture.&lt;/p&gt;

&lt;p&gt;Stay tuned for more updates as I navigate this exciting journey of leveraging AI for web application migration!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;[1] &lt;a href="https://code.visualstudio.com/docs/copilot/setup" rel="noopener noreferrer"&gt;Set up GitHub Copilot in VS Code - Visual Studio Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] &lt;a href="https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode" rel="noopener noreferrer"&gt;Use agent mode in VS Code - Visual Studio Code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Exploring the new HybridWebView for MAUI 9</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Thu, 05 Dec 2024 11:38:39 +0000</pubDate>
      <link>https://forem.com/tmcd054/exploring-the-new-hybridwebview-for-maui-9-25i9</link>
      <guid>https://forem.com/tmcd054/exploring-the-new-hybridwebview-for-maui-9-25i9</guid>
      <description>&lt;p&gt;In this blog post, I want to share my experience using the new HybridWebView for MAUI 9. The process was surprisingly easy to add a React web app into my MAUI app, and I would definitely use it again in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The HybridWebView in MAUI 9 offers a new way to integrate web content into your mobile applications. Unlike the traditional WebView hybrid approach, which requires the web app to be hosted somewhere else and running, the HybridWebView allows you to embed your web app directly into your MAUI app. This eliminates the need for separate hosting and the associated costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of HybridWebView
&lt;/h2&gt;

&lt;p&gt;Using the HybridWebView has several advantages over the standard WebView approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No Separate Hosting Required&lt;/strong&gt;: With HybridWebView, you don't need to spin up your web app separately or make it publicly available before integrating it into your MAUI app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stable Versioning&lt;/strong&gt;: You can ensure that the version of your web app embedded in your mobile app is stable and consistent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Savings&lt;/strong&gt;: By not requiring a separate hosting environment, you can save on hosting costs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Potential Drawbacks
&lt;/h2&gt;

&lt;p&gt;While the HybridWebView approach has many benefits, there are some potential drawbacks to consider:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Divergence Between Web and Mobile Apps&lt;/strong&gt;: If you can automatically push updates to your web app but have to wait for reviews in the app stores before pushing updates to your mobile app, there may be some divergence between the two versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review Delays&lt;/strong&gt;: Updates to your mobile app will still need to go through the app store review process, which can introduce delays.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How-To Guide
&lt;/h2&gt;

&lt;p&gt;The following how to will go through some of the steps I took to create a simple MAUI 9 app with a react web application&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Set up your react web application
&lt;/h3&gt;

&lt;p&gt;For this step I used web storm to create a simple vite + react web application which looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahx3fg9pkq0jwoj0i6jq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahx3fg9pkq0jwoj0i6jq.png" alt="Image of built web application in a browser" width="770" height="878"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point I have made no changes to the sample app. To generate the files you need, the sample app is enough as it generates the package.json file with the commands you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Generate your web files
&lt;/h3&gt;

&lt;p&gt;Once you have your react app created, you can run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate your files in the dist directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feo2wo2gsbcnxv5k35w2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feo2wo2gsbcnxv5k35w2u.png" alt="Dist folder with generated files" width="419" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are the files that your MAUI application will need to run your web app from the HybridWebiew.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Set Up Your MAUI Project
&lt;/h3&gt;

&lt;p&gt;At this point you should now have your web application, and the built files in the dist folder. The next step is to create your MAUI 9 app using your preferred choice of approach. For me, I use Jetbrains Rider to create my MAUI applications and this is the basic configuration&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fam39v1xt1yz7hpppy2th.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fam39v1xt1yz7hpppy2th.png" alt="New Maui project popup in rider" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have created your app, you should now have the basic “hello world app”.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Add in your web app files
&lt;/h3&gt;

&lt;p&gt;Before we can slot in the HyrbidWebView XAML element, it needs to have the supporting web app files. These files should be stored in the ‘Resources/Raw’ directory, and you can create a folder in here with any name. My example I am using is “reactApp” and once you have added in your files you should have something similar to this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5gz6wimbnxvalkznbtu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5gz6wimbnxvalkznbtu.png" alt="Web app files added into a folder in resources" width="303" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So at this point we now have our web application files added into our new MAUI 9 app and we are ready to configure our HybridWebView.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Add the HybridWebView
&lt;/h3&gt;

&lt;p&gt;For me at this stage, I just wanted to use an existing file so that the HybridWebView was the first thing that users seen when they open the app. To do this I updated the MainPage.xaml to remove the existing elements and slot in my HybridWebView element&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ContentPage&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/dotnet/2021/maui"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:x=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/winfx/2009/xaml"&lt;/span&gt;
             &lt;span class="na"&gt;x:Class=&lt;/span&gt;&lt;span class="s"&gt;"MauiApp2.MainPage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;HybridWebView&lt;/span&gt; 
        &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"FillAndExpand"&lt;/span&gt;
        &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"FillAndExpand"&lt;/span&gt;
        &lt;span class="na"&gt;HybridRoot=&lt;/span&gt;&lt;span class="s"&gt;"reactApp"&lt;/span&gt; &lt;span class="na"&gt;DefaultFile=&lt;/span&gt;&lt;span class="s"&gt;"index.html"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentPage&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing this will cause some build issues so remember to go in and remove/comment out the counter from the basic app to stop build errors&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MauiApp2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainPage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ContentPage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MainPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;InitializeComponent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Test Your Application
&lt;/h3&gt;

&lt;p&gt;Once you have made all these changes you should now be in a position to test your new MAUI 9 app with your react web application. The app ran on an iOS simulator should look something like the below screenshot&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flf3holdgd3zwvxjok4bj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flf3holdgd3zwvxjok4bj.png" alt="Screen shot of mobile application running the MAUI app" width="800" height="1734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is your react application built and running successfully hosted in your MAUI 9 application!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Overall, I really like the HybridWebView approach for integrating web content into your MAUI apps. The ease of integration, cost savings, and stable versioning make it a compelling choice. While there are some potential drawbacks, such as divergence between web and mobile app versions, the benefits far outweigh these concerns. I would definitely consider this approach for future projects.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>mobile</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>Exploring the Exciting Updates in .NET MAUI 9</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Wed, 20 Nov 2024 08:45:12 +0000</pubDate>
      <link>https://forem.com/tmcd054/exploring-the-exciting-updates-in-net-maui-9-3ga9</link>
      <guid>https://forem.com/tmcd054/exploring-the-exciting-updates-in-net-maui-9-3ga9</guid>
      <description>&lt;p&gt;I’m thrilled to share my enthusiasm for the newly released .NET MAUI version 9! As a developer who’s always eager to dive into the latest tools and technologies, I can’t wait to get hands-on with the updates and see how they can enhance my applications.&lt;/p&gt;

&lt;p&gt;One of the features I’m particularly excited about is the new &lt;strong&gt;HybridWebView&lt;/strong&gt; element. This powerful addition will allow me to embed and host my existing JavaScript web app directly within my MAUI app. The potential here is immense, especially for larger projects where the ability to lift and shift a JavaScript web app into a MAUI app could save a significant amount of time during a migration to Blazor.&lt;/p&gt;

&lt;p&gt;I’ve been experimenting with hosting a Blazor app inside my MAUI app, and while that’s been an interesting journey, the HybridWebView offers a different approach. It could simplify communication between a web app and a native app when they’re hosted and bundled together in the MAUI app, which is something I’m very interested in exploring further.&lt;/p&gt;

&lt;p&gt;Another aspect that caught my attention is the &lt;strong&gt;compiled bindings&lt;/strong&gt; approach in MAUI 9. It looks a lot more flexible and promises to streamline data binding in a way that’s both efficient and easy to manage. This is a welcome improvement, as one of the challenges I faced with MAUI 8 was the cleanup and control over bindings. The new changes in MAUI 9 for &lt;strong&gt;handler disconnection&lt;/strong&gt; will be incredibly helpful in giving me more control and visibility over my disposals, ensuring a cleaner and more maintainable codebase.&lt;/p&gt;

&lt;p&gt;For those who are as excited as I am about these updates, I encourage you to check out the official documentation to find out more: &lt;a href="https://learn.microsoft.com/en-us/dotnet/maui/whats-new/dotnet-9?view=net-maui-9.0" rel="noopener noreferrer"&gt;What’s New in .NET MAUI 9&lt;/a&gt;. I’m looking forward to playing around with all the new features and hope to post more blogs on what I discover. Stay tuned for more insights and happy coding!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Adding Platform-Specific Code to a .NET MAUI Project</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Wed, 30 Oct 2024 08:10:56 +0000</pubDate>
      <link>https://forem.com/tmcd054/adding-platform-specific-code-to-a-net-maui-project-3dh4</link>
      <guid>https://forem.com/tmcd054/adding-platform-specific-code-to-a-net-maui-project-3dh4</guid>
      <description>&lt;p&gt;.NET Multi-platform App UI (MAUI) is a powerful framework for building cross-platform applications with a single codebase. However, there are times when you need to write platform-specific code to leverage native APIs or functionalities. In this blog post, we’ll explore how to add platform-specific code to a .NET MAUI project, with examples on how to register and use this code on both Android and iOS platforms. We’ll also discuss a caveat regarding the difficulty of testing platform-specific code compared to cross-platform code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Use Platform-Specific Code?
&lt;/h3&gt;

&lt;p&gt;While .NET MAUI provides a unified API for most functionalities, certain features or optimizations might only be available on specific platforms. For instance, you might want to access a native API that isn’t exposed through MAUI, or you might need to implement platform-specific optimizations for performance reasons.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Platform-Specific Code
&lt;/h3&gt;

&lt;p&gt;To add platform-specific code in a .NET MAUI project, you can use partial classes, conditional compilation, or dependency injection. Let’s dive into each method with examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Partial Classes
&lt;/h3&gt;

&lt;p&gt;Partial classes allow you to split the implementation of a class across multiple files. This is useful for separating platform-specific code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a partial class in the shared project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Shared project&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlatformService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShowPlatformMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implement the platform-specific code in the platform projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Android project&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlatformService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShowPlatformMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MakeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="s"&gt;"Hello from Android!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToastLength&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Short&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// iOS project&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlatformService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShowPlatformMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;alert&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UIKit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UIAlertView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"Hello from iOS!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Conditional Compilation
&lt;/h3&gt;

&lt;p&gt;Conditional compilation allows you to include or exclude code based on the target platform. However, in my experience, using conditional compilation can make the code harder to read, especially in larger classes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShowPlatformMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="cp"&gt;#if ANDROID
&lt;/span&gt;    &lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MakeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello from Android!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToastLength&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Short&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="cp"&gt;#elif IOS
&lt;/span&gt;    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;alert&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UIKit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UIAlertView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from iOS!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Dependency Injection
&lt;/h3&gt;

&lt;p&gt;Dependency injection is a design pattern that allows you to inject dependencies into a class, making it easier to manage platform-specific implementations. I often found myself using a mixture of the separate file and dependency injection approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Define an interface in the shared project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Shared project&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPlatformService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShowPlatformMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implement the interface in the platform projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Android project&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AndroidPlatformService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPlatformService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShowPlatformMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MakeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello from Android!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToastLength&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Short&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// iOS project&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;iOSPlatformService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPlatformService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShowPlatformMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;alert&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UIKit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UIAlertView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from iOS!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register the platform-specific implementations in the &lt;code&gt;MauiProgram.cs&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MauiProgram.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MauiProgram&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MauiApp&lt;/span&gt; &lt;span class="nf"&gt;CreateMauiApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MauiApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;builder&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMauiApp&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="cp"&gt;#if ANDROID
&lt;/span&gt;                &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPlatformService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AndroidPlatformService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="cp"&gt;#elif IOS
&lt;/span&gt;                &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPlatformService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iOSPlatformService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;            &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the platform-specific service in your shared code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Shared project&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainPageViewModel&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IPlatformService&lt;/span&gt; &lt;span class="n"&gt;_platformService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MainPageViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IPlatformService&lt;/span&gt; &lt;span class="n"&gt;platformService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_platformService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;platformService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShowMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_platformService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ShowPlatformMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Caveat: Testing Platform-Specific Code
&lt;/h3&gt;

&lt;p&gt;One important caveat to keep in mind is that platform-specific code can be more challenging to test compared to cross-platform code. This is because platform-specific code often relies on native APIs and behaviours that are not easily replicated in a test environment. To mitigate this, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use dependency injection to abstract platform-specific code, making it easier to mock and test.&lt;/li&gt;
&lt;li&gt;Write unit tests for the shared logic and use integration tests for platform-specific code.&lt;/li&gt;
&lt;li&gt;Utilise platform-specific testing tools and frameworks to test native functionalities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Upcoming Changes in .NET MAUI 9
&lt;/h3&gt;

&lt;p&gt;In .NET MAUI 9, Microsoft is reintroducing a separate project approach for platform-specific code, which feels more like the Xamarin way. This change will likely alter how you interact with your platform-specific code, making it more organised and potentially easier to manage. This approach can help in keeping platform-specific code isolated, improving readability and maintainability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Adding platform-specific code to a .NET MAUI project allows you to leverage the full power of native APIs and functionalities. By using partial classes, conditional compilation, and dependency injection, you can effectively manage platform-specific implementations. However, be aware of the challenges in testing platform-specific code and plan your testing strategy accordingly. With the upcoming changes in .NET MAUI 9, the process of handling platform-specific code will evolve, offering a more structured approach.&lt;/p&gt;

&lt;p&gt;Happy coding! 🚀&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>maui</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Why having a page factory is a good idea</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Wed, 30 Oct 2024 08:05:43 +0000</pubDate>
      <link>https://forem.com/tmcd054/why-having-a-page-factory-is-a-good-idea-4ka6</link>
      <guid>https://forem.com/tmcd054/why-having-a-page-factory-is-a-good-idea-4ka6</guid>
      <description>&lt;h2&gt;
  
  
  Enhancing Testability in MAUI Applications with a View Factory
&lt;/h2&gt;

&lt;p&gt;In the world of software development, ensuring that our code is testable is paramount to maintaining high-quality standards. When working with .NET MAUI (Multi-platform App UI) applications, I encountered a significant challenge: making our view-models more testable. The solution I found was implementing a view factory, a technique often considered an anti-pattern but proved to be a vital piece in our development puzzle.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Testability in MVVM
&lt;/h2&gt;

&lt;p&gt;In a typical MVVM (Model-View-ViewModel) architecture, view-models are responsible for handling the logic and data-binding for views. However, injecting views directly into view-models can make unit testing cumbersome and less effective. This is because the tight coupling between views and view-models introduces dependencies that are hard to mock or isolate during testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Implementing a View Factory
&lt;/h2&gt;

&lt;p&gt;To address this, I implemented a view factory in our MAUI application. While view factories are often seen as an anti-pattern due to their potential to obscure dependencies and making code harder to understand, in our case, it was a game-changer. Here’s why:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling Views and View-Models&lt;/strong&gt;: By using a view factory, I could obtain pages through the service provider rather than injecting them directly into the view-models. This decoupling allowed us to isolate view-models from their views, making them easier to unit test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leveraging the Service Provider&lt;/strong&gt;: MAUI’s built-in dependency injection system enabled us to use the service provider to create instances of our views. This approach ensured that our view-models remained agnostic of the specific views they were associated with, further enhancing testability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Unit Testing&lt;/strong&gt;: With the view factory in place, I could mock the service provider and the views it created. This allowed us to write comprehensive unit tests for our view-models without worrying about the complexities of the UI layer. As a result, I could focus on testing the business logic and data-binding aspects of our view-models, leading to higher quality assurance.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How this looks in code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating the View Factory
&lt;/h3&gt;

&lt;p&gt;The first thing we need to do is to create a View Factory and interface it like in the examples below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyApp.Views&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ViewFactory&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IViewFactory&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IServiceProvider&lt;/span&gt; &lt;span class="n"&gt;_serviceProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ViewFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceProvider&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_serviceProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;TView&lt;/span&gt; &lt;span class="n"&gt;IViewFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateView&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TView&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TView&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TView&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyApp.Views&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IViewFactory&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;TView&lt;/span&gt; &lt;span class="n"&gt;CreateView&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TView&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code injects the IServiceProvider into the factory, and accepts a generic view type that the service provider will use to create the view&lt;/p&gt;

&lt;h3&gt;
  
  
  Plumbing this into the App
&lt;/h3&gt;

&lt;p&gt;Once we have created the ViewFactory class and interface we will then want to register it as a service in our Maui Program file like in the example below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Reference any supporting imports&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MauiProgram&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MauiApp&lt;/span&gt; &lt;span class="nf"&gt;CreateMauiApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MauiApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;builder&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMauiApp&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class="c1"&gt;// any other supporting added services&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterServices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MauiAppBuilder&lt;/span&gt; &lt;span class="nf"&gt;RegisterServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;MauiAppBuilder&lt;/span&gt; &lt;span class="n"&gt;mauiAppBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mauiAppBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IViewFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ViewFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mauiAppBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using the factory in a view-model
&lt;/h3&gt;

&lt;p&gt;Once the service is registered you can now use it in your view-models to get pages without injecting the view directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MyApp.Views&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyApp.ViewModels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PageViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ViewModelBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IViewFactory&lt;/span&gt; &lt;span class="n"&gt;_viewFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IAppNavigationService&lt;/span&gt; &lt;span class="n"&gt;_appNavigationService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PageViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IViewFactory&lt;/span&gt; &lt;span class="n"&gt;viewFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IAppNavigationService&lt;/span&gt; &lt;span class="n"&gt;appNavigationService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_viewFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_appNavigationService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;appNavigationService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RelayCommand&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;LoadNextPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_appNavigationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PushNewPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_viewFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateView&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SecondPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()).&lt;/span&gt;&lt;span class="nf"&gt;PreserveThreadContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example you can see we are injecting our IViewFactory into the view model and we are using it to create a view for our second page and pushing that to our navigation service instead of injecting the Second page into the view-model. This IViewFactory as it is injectable becomes mockable in a unit testing scenario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Recommend This Approach
&lt;/h2&gt;

&lt;p&gt;For anyone developing an MVVM-based MAUI application, I highly recommend considering the implementation of a view factory. Despite its reputation as an anti-pattern, in the context of MAUI, it can be a powerful tool to enhance the testability of your view-models. By decoupling views from view-models and leveraging the service provider, you can achieve a more modular and testable codebase.&lt;/p&gt;

&lt;p&gt;In conclusion, while the view factory might not be the go-to solution for every scenario, it proved to be invaluable in our MAUI application. It allowed us to write better unit tests, improve our code quality, and ultimately deliver a more robust application. If you’re facing similar challenges in your MAUI projects, give the view factory a try – it might just be the solution you need.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dotnetmaui</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Streamlining MVVM in .NET MAUI with Relay Commands</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Fri, 11 Oct 2024 08:18:59 +0000</pubDate>
      <link>https://forem.com/tmcd054/streamlining-mvvm-in-net-maui-with-relay-commands-ii1</link>
      <guid>https://forem.com/tmcd054/streamlining-mvvm-in-net-maui-with-relay-commands-ii1</guid>
      <description>&lt;h1&gt;
  
  
  Understanding Relay Commands in .NET MAUI
&lt;/h1&gt;

&lt;p&gt;.NET MAUI simplifies the development of cross-platform apps with a modern, consistent API. One of the features that enhances the Model-View-ViewModel (MVVM) pattern in .NET MAUI is the use of relay commands. These commands help to bind user actions on the view to methods defined in the ViewModel.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Relay Commands?
&lt;/h2&gt;

&lt;p&gt;Relay commands are a type of &lt;strong&gt;&lt;code&gt;ICommand&lt;/code&gt;&lt;/strong&gt; implementation that allows you to bind the UI events to commands in your ViewModel, eliminating the need for event handlers and making your code cleaner and more maintainable. They are particularly useful in MVVM applications where you want to keep a strict separation of concerns between the view and the ViewModel.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Relay Commands Work
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/relaycommand" rel="noopener noreferrer"&gt;The &lt;strong&gt;&lt;code&gt;RelayCommand&lt;/code&gt;&lt;/strong&gt; attribute can be used to annotate methods in a partial class, which will automatically generate the necessary command properties&lt;/a&gt;. For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RelayCommand&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GreetUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate a command property in your ViewModel that you can bind to from your view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Command Parameters and Asynchronous Commands
&lt;/h2&gt;

&lt;p&gt;Relay commands can also work with parameters and asynchronous methods. &lt;a href="https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/relaycommand" rel="noopener noreferrer"&gt;When a method has a parameter, the generated command will be of type &lt;strong&gt;&lt;code&gt;IRelayCommand&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/strong&gt;, where &lt;strong&gt;&lt;code&gt;T&lt;/code&gt;&lt;/strong&gt; is the type of the parameter1&lt;/a&gt;. &lt;a href="https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/relaycommand" rel="noopener noreferrer"&gt;For asynchronous methods, the command will implement &lt;strong&gt;&lt;code&gt;IAsyncRelayCommand&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;IAsyncRelayCommand&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/strong&gt; if it has a parameter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Prism Approach
&lt;/h2&gt;

&lt;p&gt;Prism is a well-known framework for building loosely coupled, maintainable, and testable applications in WPF, Xamarin Forms, and now in .NET MAUI. It provides developers with a robust set of tools to implement the MVVM pattern, including &lt;strong&gt;&lt;code&gt;DelegateCommand&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;CompositeCommand&lt;/code&gt;&lt;/strong&gt; for handling commands within the ViewModel. However, this approach often requires a considerable amount of boilerplate code, which can be cumbersome and increase the complexity of the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  How .NET MAUI’s MVVM Toolkit Reduces Code
&lt;/h2&gt;

&lt;p&gt;The .NET MAUI MVVM approach, powered by the Microsoft MVVM Toolkit, simplifies command implementation through the use of relay commands. This reduces the need for the verbose code that is typically associated with the Prism approach. Here’s how:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Less Boilerplate&lt;/strong&gt;: Relay commands require less boilerplate code than Prism’s &lt;strong&gt;&lt;code&gt;DelegateCommand&lt;/code&gt;&lt;/strong&gt;, as they don’t need explicit implementation of &lt;strong&gt;&lt;code&gt;CanExecute&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;Execute&lt;/code&gt;&lt;/strong&gt; methods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Event Handlers&lt;/strong&gt;: Relay commands eliminate the need for event handlers in the code-behind, which are often required in Prism to invoke commands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Command Execution&lt;/strong&gt;: The execution of commands is more straightforward with relay commands, as they allow direct method calls within the ViewModel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example: Simplified Command Implementation
&lt;/h2&gt;

&lt;p&gt;Here’s a side-by-side comparison of a command implementation using Prism and .NET MAUI’s relay commands:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Prism approach&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BindableBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;DelegateCommand&lt;/span&gt; &lt;span class="n"&gt;_myCommand&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DelegateCommand&lt;/span&gt; &lt;span class="n"&gt;MyCommand&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;_myCommand&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_myCommand&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DelegateCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExecuteCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CanExecuteCommand&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ExecuteCommand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* Command logic */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;CanExecuteCommand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* CanExecute logic */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// .NET MAUI's relay command approach&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyViewModel&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RelayCommand&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;MyCommand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Command logic here&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As demonstrated, the .NET MAUI approach results in a more concise and readable ViewModel.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Microsoft MVVM Toolkit Framework
&lt;/h2&gt;

&lt;p&gt;The Microsoft MVVM Toolkit framework is a set of tools that simplifies the implementation of the MVVM pattern in .NET applications. &lt;a href="https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm" rel="noopener noreferrer"&gt;It provides developers with a robust foundation for building responsive, maintainable, and testable applications&lt;/a&gt;. &lt;a href="https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm" rel="noopener noreferrer"&gt;The toolkit includes features like &lt;strong&gt;&lt;code&gt;ObservableObject&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;RelayCommand&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;AsyncRelayCommand&lt;/code&gt;&lt;/strong&gt;, which are essential for creating a clean and efficient ViewModel&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How .NET MAUI Adopts the MVVM Toolkit
&lt;/h2&gt;

&lt;p&gt;.NET MAUI’s MVVM approach is heavily influenced by the Microsoft MVVM Toolkit framework. &lt;a href="https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm" rel="noopener noreferrer"&gt;It leverages the toolkit’s features to streamline the development process and reduce the boilerplate code typically associated with MVVM applications&lt;/a&gt;. This allows developers to focus more on the business logic rather than the intricacies of the MVVM implementation.&lt;/p&gt;

&lt;p&gt;For example, the &lt;strong&gt;&lt;code&gt;RelayCommand&lt;/code&gt;&lt;/strong&gt; attribute in .NET MAUI is a direct adoption from the MVVM Toolkit. &lt;a href="https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm-community-toolkit-features" rel="noopener noreferrer"&gt;It automatically generates command properties, thus removing the need for manual implementation of &lt;strong&gt;&lt;code&gt;ICommand&lt;/code&gt;&lt;/strong&gt; properties and the associated &lt;strong&gt;&lt;code&gt;CanExecute&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;Execute&lt;/code&gt;&lt;/strong&gt; methods&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Using the MVVM Toolkit in .NET MAUI
&lt;/h2&gt;

&lt;p&gt;By incorporating the Microsoft MVVM Toolkit framework, .NET MAUI offers several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplified ViewModel&lt;/strong&gt;: The use of source generators and attributes provided by the MVVM Toolkit reduces the complexity of the ViewModel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Maintainability&lt;/strong&gt;: With less code to manage, the ViewModel becomes easier to maintain and evolve over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Testability&lt;/strong&gt;: The separation of concerns is more pronounced, making it easier to write unit tests for the ViewModel and Model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamlined Development&lt;/strong&gt;: Developers can quickly scaffold new features without worrying about the underlying command infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The adoption of the Microsoft MVVM Toolkit’s relay commands in .NET MAUI has taken the complexity out of the MVVM pattern. By reducing the amount of code required for command implementation, developers can enjoy a more streamlined development experience, allowing them to focus on creating feature-rich and responsive applications.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dotnetmau</category>
    </item>
    <item>
      <title>Embracing the Blazor Hybrid Approach in .NET MAUI</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Fri, 11 Oct 2024 08:01:30 +0000</pubDate>
      <link>https://forem.com/tmcd054/embracing-the-blazor-hybrid-approach-in-net-maui-49e5</link>
      <guid>https://forem.com/tmcd054/embracing-the-blazor-hybrid-approach-in-net-maui-49e5</guid>
      <description>&lt;p&gt;.NET MAUI has introduced a game-changing approach to cross-platform development with its Blazor Hybrid model. This innovative framework combines the best of both worlds: the rich ecosystem of .NET with the flexibility and familiarity of web technologies. Let’s dive into the benefits of this approach and how it can streamline your development process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging npm Packages for Style Consistency
&lt;/h2&gt;

&lt;p&gt;One of the standout features of the Blazor Hybrid approach is the ability to incorporate npm packages into your project. This is particularly beneficial when you want to maintain style consistency across different platforms. For instance, if you have a front-end library used in your web app, you can easily share the same styles in your MAUI app, ensuring a uniform look and feel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating npm into Your MAUI Project
&lt;/h3&gt;

&lt;p&gt;To add npm into your MAUI project, you’ll need to follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Initialize npm in your project directory:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm init -y&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command creates a &lt;strong&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/strong&gt; file in your project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install your desired front-end library:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install your-library-name&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Replace &lt;strong&gt;&lt;code&gt;your-library-name&lt;/code&gt;&lt;/strong&gt; with the actual library you wish to use.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build your JavaScript library:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command compiles your JavaScript and CSS assets, based on the scripts defined in your &lt;strong&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Include the compiled library in your &lt;strong&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/strong&gt; file:&lt;strong&gt;HTML&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;link rel="stylesheet" href="path/to/your-library.css"&amp;gt;&lt;br&gt;
&amp;lt;script src="path/to/your-library.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following these steps, you can seamlessly integrate front-end libraries into your MAUI app, enhancing style consistency and reducing development time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility Considerations
&lt;/h2&gt;

&lt;p&gt;Accessibility is a crucial aspect of application development. The Blazor Hybrid approach in MAUI allows developers to leverage web standards for accessibility, such as ARIA (Accessible Rich Internet Applications) attributes, ensuring that the app is usable by people with various disabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML vs. XAML: Catering to Front-End Developers
&lt;/h2&gt;

&lt;p&gt;A significant advantage of the Blazor Hybrid model is that it caters to developers who are more comfortable with HTML and CSS than XAML. This familiarity allows front-end developers to jump into MAUI development with minimal learning curve, utilizing their existing skills to create rich user interfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating BlazorWebView in XAML
&lt;/h2&gt;

&lt;p&gt;When working with the Blazor Hybrid approach in .NET MAUI, you can integrate your Blazor components directly into your XAML files using the &lt;strong&gt;&lt;code&gt;BlazorWebView&lt;/code&gt;&lt;/strong&gt; component. This allows you to leverage the full power of Blazor within your MAUI app.&lt;/p&gt;

&lt;p&gt;Here’s an example of how to add a &lt;strong&gt;&lt;code&gt;BlazorWebView&lt;/code&gt;&lt;/strong&gt; to your XAML:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ContentPage&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://xamarin.com/schemas/2014/forms"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:x=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/winfx/2009/xaml"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:local=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:YourAppNamespace"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:blazor=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui"&lt;/span&gt;
             &lt;span class="na"&gt;x:Class=&lt;/span&gt;&lt;span class="s"&gt;"YourAppNamespace.MainPage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;blazor:BlazorWebView&lt;/span&gt; &lt;span class="na"&gt;HostPage=&lt;/span&gt;&lt;span class="s"&gt;"wwwroot/index.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;blazor:BlazorWebView.RootComponents&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;blazor:RootComponent&lt;/span&gt; &lt;span class="na"&gt;Selector=&lt;/span&gt;&lt;span class="s"&gt;"#app"&lt;/span&gt; &lt;span class="na"&gt;ComponentType=&lt;/span&gt;&lt;span class="s"&gt;"{x:Type local:Main}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/blazor:BlazorWebView.RootComponents&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/blazor:BlazorWebView&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/ContentPage&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this snippet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;&lt;code&gt;xmlns:blazor&lt;/code&gt;&lt;/strong&gt; namespace is declared to reference the Blazor components.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;&lt;code&gt;BlazorWebView&lt;/code&gt;&lt;/strong&gt; tag specifies the host page that will be displayed within the MAUI app.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;&lt;code&gt;RootComponent&lt;/code&gt;&lt;/strong&gt; tag within &lt;strong&gt;&lt;code&gt;BlazorWebView.RootComponents&lt;/code&gt;&lt;/strong&gt; defines the Blazor component to be used, which is referenced by its selector (in this case, &lt;strong&gt;&lt;code&gt;#app&lt;/code&gt;&lt;/strong&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To then register the use of this package you should ensure to add it into your Maui program file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MauiProgram&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MauiApp&lt;/span&gt; &lt;span class="nf"&gt;CreateMauiApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MauiApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;builder&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMauiApp&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;

        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMauiBlazorWebView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding this code to your MAUI app, you can seamlessly integrate Blazor components, enabling you to use standard web technologies like HTML and CSS for your UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigating the Drawbacks
&lt;/h2&gt;

&lt;p&gt;While the Blazor Hybrid approach offers numerous benefits, it’s not without its drawbacks. Navigation can become more complex, especially when dealing with navigation inside a Blazor WebView and between navigation pages within the app. Developers need to carefully manage routing to ensure a smooth user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Blazor Hybrid approach in .NET MAUI presents a compelling option for developers looking to create cross-platform applications with shared styles and reduced development time. By embracing web technologies, developers can build accessible, consistent, and visually appealing apps while leveraging their existing front-end expertise. Despite some challenges with navigation, the benefits of this approach make it a worthy consideration for your next project.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dotnetmaui</category>
      <category>blazor</category>
      <category>maui</category>
    </item>
    <item>
      <title>Migrating from Xamarin to .NET MAUI: A Journey to Modernization</title>
      <dc:creator>Thomas M</dc:creator>
      <pubDate>Fri, 11 Oct 2024 07:59:33 +0000</pubDate>
      <link>https://forem.com/tmcd054/migrating-from-xamarin-to-net-maui-a-journey-to-modernization-109n</link>
      <guid>https://forem.com/tmcd054/migrating-from-xamarin-to-net-maui-a-journey-to-modernization-109n</guid>
      <description>&lt;p&gt;As the Xamarin framework approached its end of life in May 2024, the need to migrate applications to a more current and supported framework became a pressing concern for developers seeking continued security updates and access to new features. This blog post outlines my journey of migrating an application from Xamarin to .NET MAUI, the challenges faced, and the strategic decisions that led to a successful transition.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Impetus for Migration
&lt;/h2&gt;

&lt;p&gt;The announcement of Xamarin’s end of life set the stage for a necessary migration. The primary goal was to ensure that the application remained within a supported ecosystem, benefiting from &lt;strong&gt;security updates&lt;/strong&gt; and the &lt;strong&gt;rich new features&lt;/strong&gt; that come with active development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluating Migration Approaches
&lt;/h2&gt;

&lt;p&gt;Initially, I explored several migration strategies, including the use of automated migration tools. However, these tools often resulted in a multitude of errors and a fragmented codebase that was difficult to manage. The scattered approach left much to be desired, prompting a reevaluation of the strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Embracing a Full Rewrite in .NET MAUI
&lt;/h2&gt;

&lt;p&gt;After careful consideration, I decided to undertake a full rewrite of the application code into the .NET MAUI framework. This approach allowed for a clearer migration path, enabling me to move the application in sections and conduct &lt;strong&gt;incremental testing&lt;/strong&gt;. It also provided an opportunity to address and fix many of the issues that were problematic in Xamarin, resulting in a more robust and reliable application.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Shift to Blazor Hybrid and MVVM
&lt;/h2&gt;

&lt;p&gt;The migration journey was also an opportune time to modernize the application’s architecture. I chose to adopt the &lt;strong&gt;Blazor Hybrid approach&lt;/strong&gt;, which allowed for the use of HTML and CSS—technologies that are widely familiar to many developers, more so than XAML. This shift not only enhanced the developer experience but also improved the application’s maintainability and scalability.&lt;/p&gt;

&lt;p&gt;Furthermore, the transition to .NET MAUI was complemented by a move from the MVP (Model-View-Presenter) pattern to an &lt;strong&gt;MVVM (Model-View-ViewModel) architecture&lt;/strong&gt; using the &lt;strong&gt;MVVM Toolkit&lt;/strong&gt;. This change facilitated a cleaner separation of concerns, improved testability, and allowed for more intuitive data binding and UI updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Outcome and Future Insights
&lt;/h2&gt;

&lt;p&gt;The decision to rewrite the application and embrace a Blazor Hybrid model with MVVM was not without its challenges and required a significant investment of time. However, the outcome has been overwhelmingly positive. The application now stands on a modern, scalable, and flexible foundation, ready to evolve with the ever-changing landscape of technology.&lt;/p&gt;

&lt;p&gt;I am very pleased with the results of this migration and look forward to sharing more insights in future blog posts. I’ll delve into the benefits and any obstacles encountered during this transition, providing a comprehensive look at the migration process for those considering a similar path.&lt;/p&gt;

&lt;p&gt;Stay tuned for more updates on this journey of application transformation.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dotnetmaui</category>
      <category>blazor</category>
    </item>
  </channel>
</rss>
