<?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>DEV Community: Laura Pučkoriūtė</title>
    <description>The latest articles on DEV Community by Laura Pučkoriūtė (@laura_puckoriute).</description>
    <link>https://dev.to/laura_puckoriute</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%2F2135541%2Fab493584-4613-486a-80f4-b08eb318e740.png</url>
      <title>DEV Community: Laura Pučkoriūtė</title>
      <link>https://dev.to/laura_puckoriute</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/laura_puckoriute"/>
    <language>en</language>
    <item>
      <title>Event-Driven Architecture: When and How?</title>
      <dc:creator>Laura Pučkoriūtė</dc:creator>
      <pubDate>Tue, 27 Jan 2026 17:55:47 +0000</pubDate>
      <link>https://dev.to/laura_puckoriute/event-driven-architecture-when-and-how-4pn</link>
      <guid>https://dev.to/laura_puckoriute/event-driven-architecture-when-and-how-4pn</guid>
      <description>&lt;p&gt;After spending the last three years building high-assurance event-driven systems in healthcare and recently diving deep into Domain-Driven Design (DDD), I want to bridge these two worlds by breaking down the core concepts of Event-Driven Architecture. If you are brand new to EDA, I invite you to use this blog post as a starting point. Let’s dive into the basics!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event-Driven Architecture (EDA)&lt;/strong&gt; is a paradigm that works with creating, detecting, and consuming events. In &lt;strong&gt;Microservice Architecture&lt;/strong&gt;, services act either as producers of those events, consumers, or both. Business and domain needs dictate whether these architecture patterns should be selected.&lt;/p&gt;

&lt;p&gt;There are a few reasons why you might choose an event-driven design for your system.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Asynchronous communication:&lt;/strong&gt; You might be designing a system where you want to avoid long wait times for something to happen. Then you could allow one part of your system to proceed rather than waiting for a response.&lt;/p&gt;

&lt;p&gt;For example, you are designing a library book reservation system with a high number of users. While it may seem unrealistic, let’s imagine hundreds of book reservations or reservation cancellations per second. You want to let the user know that the book has been reserved instead of making the user wait for a few seconds or longer until the reservation database is actually updated. If you choose Event-Driven Architecture (EDA), your service that interacts with the user in this scenario is not blocked and doesn’t need to wait for their book to be reserved. We trust that the architecture will handle the user’s request and eventually reserve their book.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decoupled systems:&lt;/strong&gt; If you are implementing EDA in a microservice architecture, you can clearly see how event producer and consumer services operate independently. The separate management of these services is crucial for their scalability and adaptability to changes in business requirements.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Business logic across multiple consumers:&lt;/strong&gt; You might find that your business requirements ask you to ensure that when one type of event happens, multiple other services can pick the process up from there and decide independently how to respond to specific events. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Can EDA be used in monolithic applications?
&lt;/h2&gt;

&lt;p&gt;Yes, it can. It means the same application/service will be responsible for producing and consuming the events. It depends on the business requirements. One thing that monolithic architecture does not offer is physical decoupling of services. In microservice architecture, the event producer and consumer services are truly independent of each other. If something fails in the consumer service, for example, the producer and event backbone systems stay healthy. Once the consumer service is repaired and running smoothly, it can pick up the events from the queue or an event stream (event backbone), ensuring data reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are producers, consumers, and the event backbone?
&lt;/h2&gt;

&lt;p&gt;It’s very simple. A producer is a service that creates the event. A consumer is a service that receives the event and reacts to it. And the event backbone is a service that sits in between the two and stores those events, almost like a mailbox.&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%2Fu5w8kztqg41zwqk567ld.jpg" 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%2Fu5w8kztqg41zwqk567ld.jpg" alt="Event-Driven Architecture Diagram" width="531" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s say I have to design a system that deals with a high number of book reservations. After conversations with the business team in order to understand the domain, I have identified one of the events - the user reserved a book. That means there should be a producer service that creates the event &lt;code&gt;BookReserved&lt;/code&gt;. This event should hold information about which user reserved the book and what book that is. The producer service pushes the event to the event backbone system that holds many &lt;code&gt;BookReserved&lt;/code&gt; events. &lt;/p&gt;

&lt;p&gt;I also have a third service - consumer. My consumer service is set up in a way that is constantly listening to the event backbone system to see if there are any new events. Once it picks up the &lt;code&gt;BookReserved&lt;/code&gt; event, the consumer reads information about the book and the user. Then the consumer calls certain functions that mark the reservation in the database, change the status of that specific book instance, and similar.&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%2Fs18rwbb57ym0z59qptkm.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%2Fs18rwbb57ym0z59qptkm.png" alt="Event-Driven Architecture Diagram of Book Reservation System" width="800" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see from the diagram above, I selected my producer and consumer services to be implemented in .NET. Of course, it could be any other framework that fits your business needs and the team’s skillset.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the event backbone work?
&lt;/h2&gt;

&lt;p&gt;The event backbone is the core of EDA. It is a crucial infrastructure component that can be implemented as an event bus, event broker, event router, or event hub (you may have heard some of these names).&lt;/p&gt;

&lt;p&gt;The event backbone is the medium through which events produced by one component (the producer) are communicated to other components (the consumers). It ensures that when an event occurs, relevant consumers are notified and can act upon it independently.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Producers inform the event backbone about events of interest.&lt;/li&gt;
&lt;li&gt;The event backbone routes these events to the appropriate consumers based on their subscriptions.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Many technologies implement the event backbone mechanism. The choice of which one to pick depends on how you want the events to be processed. Should we treat the events as a &lt;strong&gt;temporary message&lt;/strong&gt; to be delivered or a &lt;strong&gt;permanent record&lt;/strong&gt; to be stored?&lt;/p&gt;

&lt;p&gt;Thinking of the messages in terms of their life span really helped me understand the difference between various technology options.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Event data purpose&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Technology category&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Event Backbone Tools&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Temporary Message&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Message Brokers&lt;/td&gt;
&lt;td&gt;RabbitMQ, ActiveMQ, AWS SQS / Azure Service Bus, NATS, IBM MQ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Semi-Permanent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Event Streaming&lt;/td&gt;
&lt;td&gt;Apache Kafka, Redpanda, Apache Pulsar, Azure Event Hubs / AWS Kinesis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Permanent record&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Event Sourcing&lt;/td&gt;
&lt;td&gt;EventStoreDB, Marten, Axon Server, Eventuate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The specific code you write for your producers and consumers to integrate with the event backbone is traditionally tied to the backbone technology you choose. Using a tool's &lt;strong&gt;native SDK&lt;/strong&gt; means your code must follow a specific protocol, whether it's RabbitMQ's exchanges or Kafka's partitions.&lt;/p&gt;

&lt;p&gt;There exist backbone-agnostic libraries, like Wolverine in .NET. They act as an abstraction layer between the code and infrastructure. You would set the right configuration, and then write generic handlers while letting the Wolverine framework manage the underlying connection to the event backbone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Event-Driven Architecture allows microservices to operate independently, reducing latency and preventing system-wide bottlenecks. However, the most critical takeaway isn't the tech stack. It's the &lt;strong&gt;alignment with your domain.&lt;/strong&gt; Whether you choose a Message Broker for instant tasks or Event Streaming for a historical record, the decision must start with your business requirements. By understanding how your data needs to live and move, you can build a system that isn't just "event-driven," but purpose-driven.&lt;/p&gt;

</description>
      <category>eventdriven</category>
      <category>microservices</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Certified! Azure AI-102: AI Engineer Associate</title>
      <dc:creator>Laura Pučkoriūtė</dc:creator>
      <pubDate>Thu, 12 Jun 2025 17:00:58 +0000</pubDate>
      <link>https://dev.to/laura_puckoriute/certified-azure-ai-102-ai-engineer-associate-18k8</link>
      <guid>https://dev.to/laura_puckoriute/certified-azure-ai-102-ai-engineer-associate-18k8</guid>
      <description>&lt;p&gt;In June 2025, I took and passed the &lt;a href="https://learn.microsoft.com/en-us/credentials/certifications/azure-ai-engineer/?practice-assessment-type=certification" rel="noopener noreferrer"&gt;Azure AI-102 certification&lt;/a&gt; exam. I would like to share my experience and what might be useful for someone else on this journey.&lt;/p&gt;

&lt;p&gt;As AI is inevitably becoming the new game of the industry, naturally, many will want to learn more about it. My key driver was to learn what’s possible. Before my studies started, I had seen multiple PoCs, heard of ideas, and seen extraordinary AI applications changing people’s lives in many areas. But this certification focused on the Azure services was supposed to empower me to participate in conversations and ideation processes for AI-powered applications, to help me think creatively, consider ethical accountabilities, and business needs. And so it enabled me to do this.&lt;/p&gt;

&lt;h1&gt;
  
  
  My study journey
&lt;/h1&gt;

&lt;p&gt;To set the context, the time frame of my preparation was probably not ideal due to the recent changes to the content on the 30th of April. On the 1st of May, I first looked at the content and started my study path, and a month later, I took the exam. I followed the &lt;a href="https://learn.microsoft.com/en-us/training/courses/ai-102t00" rel="noopener noreferrer"&gt;official learning path&lt;/a&gt; for the certification, and I would conclude that the path is designed carefully. It’s straightforward and should be your main source for study. It includes quizzes and hands-on labs, which were very valuable.&lt;/p&gt;

&lt;p&gt;Although the MS Learn path for Azure AI-102 certification was long and had good coverage, I am glad I also spent time reading the official documentation found on the MS Learn website.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using AI to learn about AI
&lt;/h3&gt;

&lt;p&gt;Because I felt that I couldn’t remember everything and my deadline for the exam was a bit tight, in the last week or so, I decided to ask &lt;strong&gt;Google Gemini&lt;/strong&gt; to generate certification-style questions and make a quiz for me based on the content in the documentation that I was revising. I did this not only because I wanted focused revision on specific topics, but also because there are so few resources out there for mock exams of this specific certification. These Gemini-generated quizzes are what got me into an assessment mode and even helped me get into the habit of using intuition when I couldn’t rely on the confidence of my knowledge.&lt;/p&gt;

&lt;p&gt;Whatever your reason might be, it could be a useful approach. But never abandon the act of actually reading the documentation, and ensure that your AI chat is not hallucinating by grounding it on the exact content you are interested in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a 10-question quiz based only on the content in this documentation: https://docs.azure.cn/en-us/ai-services/language-service/question-answering/overview

Follow the Azure certification exam question style:
You have a question answering project.
A customer asks a question that is not part of the project.
You review the active learning suggestions and do not see any suggestions.
You need to ensure that customer questions are included in the active learning suggestions. The solution must minimize administrative effort.
What should you do?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Practice material online
&lt;/h3&gt;

&lt;p&gt;📚 If you have access to &lt;a href="https://learning.oreilly.com/certifications/9780135475713/" rel="noopener noreferrer"&gt;O’Reilly&lt;/a&gt; resources, this resource provides revision questions. Although the questions are ridiculously easy compared to the actual exam.&lt;/p&gt;

&lt;p&gt;📚 &lt;a href="https://www.whizlabs.com/microsoft-azure-certification-ai-102/" rel="noopener noreferrer"&gt;Whizlabs&lt;/a&gt; also has some practice tests available that are more in the format of the exam. I am not sure how up-to-date they are since the last change to the exam, but at the moment, you can Try Free Test of 15 questions.&lt;/p&gt;

&lt;p&gt;📚 &lt;a href="https://www.udemy.com/course/azure-ai-engineer-associate-ai-102-practice-tests-new/" rel="noopener noreferrer"&gt;Udemy&lt;/a&gt; has practice tests with questions similar to those in the actual exam.&lt;/p&gt;

&lt;p&gt;You don’t need them all. For me, doing the free test on Wizlabs was enough to get the gist of what to expect. But I imagine if I had purchased more of those tests, I would have felt maybe a bit more confident.&lt;/p&gt;

&lt;p&gt;And most importantly, take the practice assessment provided on the &lt;a href="https://learn.microsoft.com/en-us/credentials/certifications/azure-ai-engineer/?practice-assessment-type=certification" rel="noopener noreferrer"&gt;official certification page&lt;/a&gt;. It will generate a report that you can use to identify your weaker sections and will even create a revision plan for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  My time
&lt;/h3&gt;

&lt;p&gt;In total, I spent 8 full work days -  about 7 hours each, going through the learning path. And then about 3 hours for 8 more days revising, looking into more details in the documentation, repeating the lab exercises, and quizzing myself.&lt;/p&gt;




&lt;h1&gt;
  
  
  How was the exam?
&lt;/h1&gt;

&lt;p&gt;I took my exam online. I had never done this before, but the overall setup and check-in process went smoother than I expected. As long as you have completed the system check on your machine before the exam, you can be calm and enjoy the comfort of your home during the assessment.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The questions in the exam included:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;single-choice and multiple-choice questions&lt;/li&gt;
&lt;li&gt;code/json completion exercises with dropdown options&lt;/li&gt;
&lt;li&gt;building lists in the right order&lt;/li&gt;
&lt;li&gt;one &lt;a href="https://learn.microsoft.com/en-us/credentials/support/exam-duration-exam-experience#hot-area-017" rel="noopener noreferrer"&gt;Hot Area&lt;/a&gt; question to determine what I would click to achieve an objective&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/credentials/support/exam-duration-exam-experience#mark-review-033" rel="noopener noreferrer"&gt;Mark Review&lt;/a&gt; questions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/credentials/support/exam-duration-exam-experience#case-studies-113" rel="noopener noreferrer"&gt;Case Studies&lt;/a&gt; questions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There was no lab exercise during my exam.&lt;/p&gt;

&lt;p&gt;At the end, there were six questions dedicated to one case study. You will see it clearly in the progress bar when you get to them. Don’t forget to use the buttons in the left pane to expand and read the case study information!&lt;/p&gt;

&lt;h3&gt;
  
  
  MS Learn
&lt;/h3&gt;

&lt;p&gt;You can also access &lt;a href="https://learn.microsoft.com/en-us/" rel="noopener noreferrer"&gt;Microsoft Learn documentation&lt;/a&gt; during the assessment, but be extremely mindful of your time. I used it for a couple of questions, and even then only quickly scanned through the pages. There is a limit of maybe 5 tabs that you can open. &lt;a href="https://learn.microsoft.com/en-us/credentials/support/exam-duration-exam-experience#accessing-microsoft-learn-during-your-certification-exam" rel="noopener noreferrer"&gt;See how you can access the documentation.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Exam time
&lt;/h3&gt;

&lt;p&gt;From the moment you start the assessment, you will see a timer counting down the minutes you have left to finish the exam. You have 100 minutes + 20 minutes more, which includes a break and any adjustments in case you need them. The time you see counts for both parts of the assessment - the first ~50 questions, as well as the last 6 questions dedicated to the case study. It also includes the review time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Difficulty
&lt;/h3&gt;

&lt;p&gt;The difficulty of the questions was what I expected - difficult. I had to use intuition a lot and have internal conversations about why I chose this answer over the other. It’s the little details in the question that matter. I didn’t know everything and didn’t have time to look up the documentation, but as long as I had an overall knowledge of the services, experience with the labs, and common sense, I was able to answer with some confidence.&lt;/p&gt;
&lt;h3&gt;
  
  
  If you want to learn from my mistakes:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep up the pace&lt;/strong&gt; during your assessment - time flies by when you’re having fun ;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expand for more information&lt;/strong&gt; in the last case study questions - I forgot it was a thing pressured under the time limit&lt;/li&gt;
&lt;li&gt;If you want to keep tissues or chewing gum on your desk, take them out of the packaging&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Exam day
&lt;/h1&gt;

&lt;p&gt;The weekend before the exam, I went dancing (one of my passions). Some friends told me to study instead, but I know what works for me. Study and practice early, not the night before. My rule of thumb - from 3 pm on the day before the exam, I am not allowed to study, take practice tests, or revise. It’s rest time.&lt;/p&gt;

&lt;p&gt;You might not know everything that’s in the exam. You might not feel confident. But you stuck to your plan, and que sera, sera. &lt;strong&gt;You are capable&lt;/strong&gt;, you’ve done the work, find your peace.&lt;/p&gt;

&lt;p&gt;I took my exam online, and when I saw the page with my results indicating that I successfully received my certification, I sighed with relief and thought of my exam proctor - “Is he proud of me?”&lt;/p&gt;

&lt;p&gt;This is what I wish for you - may your proctor be proud of you. Good luck with your studies. &lt;strong&gt;You’ve got this!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>azure</category>
      <category>certification</category>
    </item>
    <item>
      <title>JavaScript Bridge in X-Platform MAUI app</title>
      <dc:creator>Laura Pučkoriūtė</dc:creator>
      <pubDate>Sun, 26 Jan 2025 20:02:34 +0000</pubDate>
      <link>https://dev.to/laura_puckoriute/javascript-bridge-in-x-platform-maui-app-25kk</link>
      <guid>https://dev.to/laura_puckoriute/javascript-bridge-in-x-platform-maui-app-25kk</guid>
      <description>&lt;p&gt;JavaScript Bridge is a way to allow communication between the native MAUI app and a web application rendered in a &lt;code&gt;WebView&lt;/code&gt;. It enables a seamless two-way interaction, allowing native functionality to interact with web content and vice versa.&lt;/p&gt;

&lt;p&gt;With a JavaScript bridge, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable two-way communication between the native and the web applications&lt;/li&gt;
&lt;li&gt;Access native device functionality from the web app&lt;/li&gt;
&lt;li&gt;Reuse the existing web application in your MAUI project and avoid code duplication&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  MAUI to Web communication
&lt;/h1&gt;

&lt;h2&gt;
  
  
  MAUI project code
&lt;/h2&gt;

&lt;p&gt;You’ve got your Web application rendered in a WebView in the MAUI project. Now if you want to call a JavaScript function defined on the Web Application, you can simply execute code like this from your WebView page:&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;// Call JavaScript from the WebView&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;myWebView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EvaluateJavaScriptAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"window.nativeAppCallbacks.updateNativeSettings(data)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script parameter can be anything you want. A simple &lt;code&gt;alert&lt;/code&gt; function or something specifically defined for the MAUI to Web communication in your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web project code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeAppCallbacks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;updateNativeSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// some code to update the state&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;h1&gt;
  
  
  Web to MAUI communication
&lt;/h1&gt;

&lt;h2&gt;
  
  
  MAUI project code
&lt;/h2&gt;

&lt;p&gt;If your MAUI application is cross-platform, for example, it can be run on both Android and iOS operating systems, you’ll want to create two platform-specific services defining the functionality that can be executed from the web app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Android
&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;using&lt;/span&gt; &lt;span class="nn"&gt;Android.Webkit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Java.Interop&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.Platforms.Android&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;JavascriptBridge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoggedInWebView&lt;/span&gt; &lt;span class="n"&gt;loggedInWebView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;JavascriptInterface&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fetchNativeAppSettings"&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;FetchNativeAppSettings&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;InvokeOnMainThreadAsync&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MyWebView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FetchNativeAppSettings&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;[JavascriptInterface]&lt;/code&gt; attribute is used to mark a class or interface in your Android MAUI project so that it can be accessed by JavaScript running in a &lt;code&gt;WebView&lt;/code&gt;. It tells the Android runtime that this class or object will serve as a bridge between JavaScript and native code. Without this attribute, the methods in the class cannot be invoked from JavaScript.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;[Export("methodName")]&lt;/code&gt; attribute is applied to methods within a class to specify the exact method name the web project will use to call this function.&lt;/p&gt;

&lt;p&gt;Then, you can create a HandlerService class which will be used to register your new JavaScript bridge on a WebView.&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;Microsoft.Maui.Handlers&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.Platforms&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;MyWebViewHandlerService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IMyWebViewHandlerService&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;SetHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IWebViewHandler&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IWebView&lt;/span&gt; &lt;span class="n"&gt;webView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PlatformView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JavaScriptEnabled&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;javascriptBridge&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;JavascriptBridge&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;MyWebView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;webView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PlatformView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJavascriptInterface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;javascriptBridge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"nativeApp"&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;Please note, that although the namespace does not specify the platform used, this code should be in the Android folder. The namespace is inaccurate here so that the code can be accessible from shared non-platform-specific folders. For example, from the page class itself.&lt;/p&gt;

&lt;p&gt;Wherever in your MAUI app code you deal with the &lt;code&gt;IWebViewHandler&lt;/code&gt; , you should enable JavaScript execution for your WebView, which for security reasons by default is disabled. Most importantly, add your JavaScript bridge to the Android-specific WebView control &lt;code&gt;handler.PlatformView&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nativeApp&lt;/code&gt;  is a JavaScript object which will contain all the JS functions defined in the bridge. So the web app can call &lt;code&gt;nativeApp.fetchNativeAppSettings()&lt;/code&gt; .&lt;/p&gt;

&lt;h3&gt;
  
  
  iOS
&lt;/h3&gt;

&lt;p&gt;Implementing the JavaScript on the iOS side is slightly different, but not too complicated.&lt;/p&gt;

&lt;p&gt;Again, you want to create a JavaScript bridge class, just like for Android.&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;Foundation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;WebKit&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.Platforms.iOS&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;JavascriptBridge&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NSObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IWKScriptMessageHandler&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;WeakReference&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyWebView&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_myWebViewRenderer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="nf"&gt;JavascriptBridge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyWebView&lt;/span&gt; &lt;span class="n"&gt;myWebView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_myWebViewRenderer&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;WeakReference&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyWebView&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;myWebView&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;DidReceiveScriptMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;WKUserContentController&lt;/span&gt; &lt;span class="n"&gt;userContentController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;WKScriptMessage&lt;/span&gt; &lt;span class="n"&gt;message&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;_myWebViewRenderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetTarget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;switch&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="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"fetchNativeAppSettings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FetchNativeAppSettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;break&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;:&lt;/span&gt;
                &lt;span class="k"&gt;return&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;iOS JS Bridge has to extend &lt;code&gt;IWKScriptMessageHandler&lt;/code&gt; and implement its member &lt;code&gt;DidReceiveScriptMessage&lt;/code&gt;. Whenever the JavaScript function is called, &lt;code&gt;DidReceiveScriptMessage&lt;/code&gt; will be executed. This is where we check which JavaScript function was called and act accordingly. We can get the JavaScript function name with &lt;code&gt;message.Name&lt;/code&gt; and the parameters by looking at the &lt;code&gt;message.Body&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Great, now let’s create a HandlerService in the iOS folder which will be used to expose all these functions from the bridge to the WebView. This has to be done by building a string with actual JavaScript 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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Foundation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Maui.Handlers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;WebKit&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.Platforms&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;MyWebViewHandlerService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IMyWebViewHandlerService&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;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;JavascriptCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; 
          &lt;span class="s"&gt;"""
&lt;/span&gt;                &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nativeApp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
                &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nativeApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchNativeAppSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webkit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messageHandlers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchNativeAppSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IWebViewHandler&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IWebView&lt;/span&gt; &lt;span class="n"&gt;webView&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;javascriptBridge&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;MyJavascriptBridge&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;MyWebView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;webView&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;script&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;WKUserScript&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;NSString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JavaScriptCode&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;WKUserScriptInjectionTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AtDocumentEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PlatformView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserContentController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUserScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PlatformView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserContentController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddScriptMessageHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;javascriptBridge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"fetchNativeAppSettings"&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;This code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;creates JavaScript code string which defines what happens when the Web app makes calls &lt;code&gt;window.nativeApp.fetchNativeAppSettings()&lt;/code&gt; function. When that happens, it will post a message containing the name of the function and parameters to a &lt;code&gt;fetchNativeAppSettings&lt;/code&gt; handler&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AddUserScript&lt;/code&gt; adds this JavaScript code to the iOS-specific platform controller&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AddScriptMessageHandler&lt;/code&gt; installs a message handler that we call from the script (&lt;code&gt;JavascriptCode&lt;/code&gt; ) that we just added to the controller&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Web project code
&lt;/h2&gt;

&lt;p&gt;What your web code looks like will depend on your Web App Framework. Most likely, you’ll have a JavaScript code that is something like this. It’s that simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeApp&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchNativeAppSettings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchNativeAppSettings&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;h1&gt;
  
  
  Benefit during migration
&lt;/h1&gt;

&lt;p&gt;JavaScript bridge is an excellent and pretty straightforward way to enable two-way communication for your native app rendering a web one.&lt;/p&gt;

&lt;p&gt;On my project, we performed a Xamarin to MAUI cross-platform native app migration. This is when we implemented a MAUI JavaScript bridge for interaction with our Vue app. We found that we didn’t need to change a single line of code in the web project.&lt;/p&gt;

&lt;p&gt;Currently, we are looking to migrate from Vue to the Blazor Web application. Having done some investigation and written a bit of code, it’s clear to see that although we’ll have a completely new and different .NET web application, there will be no changes that we’ll have to make to the native MAUI app codebase.&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%2Fif49kbcoadg2k1pqdr1o.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%2Fif49kbcoadg2k1pqdr1o.png" alt=" " width="540" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Readings ✨
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/webview?view=net-maui-8.0&amp;amp;pivots=devices-android#invoke-javascript" rel="noopener noreferrer"&gt;Invoking JavaScript in MAUI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/maui/user-interface/handlers/?view=net-maui-8.0" rel="noopener noreferrer"&gt;Understanding handlers in MAUI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please look at the Apple Documentation for more details on &lt;a href="https://developer.apple.com/documentation/webkit/wkuserscript" rel="noopener noreferrer"&gt;JavaScript message handlers&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why Your .NET Application Works in an IDE but Fails in CI/CD or Terminal: Understanding Asynchronous Execution</title>
      <dc:creator>Laura Pučkoriūtė</dc:creator>
      <pubDate>Fri, 06 Dec 2024 17:16:33 +0000</pubDate>
      <link>https://dev.to/laura_puckoriute/why-your-net-application-works-in-an-ide-but-fails-in-cicd-or-terminal-understanding-3cfj</link>
      <guid>https://dev.to/laura_puckoriute/why-your-net-application-works-in-an-ide-but-fails-in-cicd-or-terminal-understanding-3cfj</guid>
      <description>&lt;p&gt;Yup, I missed an &lt;code&gt;await&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;Nothing new. No one else to blame.&lt;/p&gt;

&lt;p&gt;But why did it take so long to find it? Because the app worked locally but failed on the pipeline, I thought it was a pipeline problem! Is the Azure pipeline set up in some way that doesn’t wait for my &lt;code&gt;dotnet run&lt;/code&gt; command to finish? Does an error occur that is not properly logged and therefore not visible on the pipeline run? Do I need to use a different Azure &lt;code&gt;DotNetCoreCLI&lt;/code&gt; task or instead start the program with a shell script? Am I losing my mind?&lt;/p&gt;

&lt;p&gt;Asynchronous programming can be tricky, especially when tasks are not awaited properly. This often leads to unexpected behavior, especially when running applications in different environments (like &lt;strong&gt;Rider&lt;/strong&gt; or &lt;strong&gt;other IDEs&lt;/strong&gt; vs. the &lt;strong&gt;terminal&lt;/strong&gt; or &lt;strong&gt;CI/CD pipelines&lt;/strong&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  My case
&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;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;SomeProcess&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Missing await&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&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;SomeProcess&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="nf"&gt;SomeOtherProcess&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;My &lt;code&gt;Main&lt;/code&gt; function called a process that had a few asynchronous tasks. On the pipeline, observing the logs, I saw that the pipeline task running the program would be completed at random times, rarely letting the .NET program complete a full run.&lt;/p&gt;

&lt;p&gt;That is because the call in the &lt;code&gt;Main&lt;/code&gt; function to a process initiating asynchronous tasks never waited for that process to complete, and the &lt;code&gt;Main&lt;/code&gt; function run finished whenever it pleased.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But why did the .NET program always run fully in the Rider IDE?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Seems like &lt;strong&gt;Rider&lt;/strong&gt; might have a specific run environment. Rider might implicitly handle unawaited tasks, potentially waiting for asynchronous tasks to complete before terminating the run process, allowing the app to seem to run fully. An &lt;strong&gt;IDE&lt;/strong&gt; might introduce additional debugging or process-wait mechanisms that make the application appear to be more forgiving in certain cases.&lt;/p&gt;

&lt;p&gt;When running in the &lt;strong&gt;terminal&lt;/strong&gt; or &lt;strong&gt;CI/CD pipelines&lt;/strong&gt;, there is no "help" from Rider or other IDEs. The application exits immediately after the &lt;code&gt;Main&lt;/code&gt; method finishes, &lt;strong&gt;even if tasks are still running in the background&lt;/strong&gt;. Without &lt;code&gt;await&lt;/code&gt;, these background tasks won’t finish before the application ends, leading to errors or incomplete operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I fixed it
&lt;/h3&gt;

&lt;p&gt;First, it was important to start my application by running the &lt;code&gt;dotnet run&lt;/code&gt; command from the &lt;strong&gt;terminal&lt;/strong&gt;, and not an IDE. This way it was clear that the problem was &lt;strong&gt;in the application code&lt;/strong&gt;, not in my pipeline setup.&lt;/p&gt;

&lt;p&gt;Then, I had to dig a bit through the code to find where exactly I was not handling the asynchronous processes properly. Once I found it, I added the missing &lt;code&gt;await&lt;/code&gt; statement. And it worked like magic.&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;static&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;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&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="nf"&gt;SomeProcess&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&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;SomeProcess&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="nf"&gt;SomeOtherProcess&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;
  
  
  Lessons learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Always &lt;code&gt;await&lt;/code&gt; asynchronous operations.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test in multiple environments&lt;/strong&gt;: test the run of your application in a terminal or similar environment to where it is failing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not rely just on the IDE run processes&lt;/strong&gt; to verify the functionality of your program.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>cicd</category>
      <category>pipeline</category>
    </item>
    <item>
      <title>Receiving Push Notifications from Firebase in Android MAUI</title>
      <dc:creator>Laura Pučkoriūtė</dc:creator>
      <pubDate>Fri, 27 Sep 2024 15:12:37 +0000</pubDate>
      <link>https://dev.to/laura_puckoriute/receiving-push-notifications-from-firebase-in-android-maui-1ho</link>
      <guid>https://dev.to/laura_puckoriute/receiving-push-notifications-from-firebase-in-android-maui-1ho</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;My team has recently implemented push notifications for our Android MAUI (.NET Multi-platform Application UI) app. This implementation was part of our ongoing migration from Xamarin to MAUI in a cross-platform mobile healthcare application. The app is designed to help users manage their health by providing timely information, reminders, and updates.&lt;/p&gt;

&lt;p&gt;One of the key features of this healthcare app is its ability to receive push notifications that include links directing users to specific pages within the app. This functionality ensures that users can quickly and easily access relevant information or take necessary actions directly from the notifications.&lt;/p&gt;

&lt;p&gt;To power this feature, we are utilizing Firebase as the backend service for sending out messages. &lt;a href="https://firebase.google.com/docs/cloud-messaging" rel="noopener noreferrer"&gt;Firebase Cloud Messaging&lt;/a&gt; service provides a reliable and scalable solution for managing and delivering push notifications.&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%2F6igohi9tyqtd2od4btmv.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%2F6igohi9tyqtd2od4btmv.png" alt="A prototype picture of an a mobile device with a push notification displayed on the screen" width="465" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To provide some context, this diagram represents the part of our system responsible for sending push notifications. As you can see, we utilize Azure Notification Hub to handle this functionality.&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%2Fpx992mkodop7tx2kuuxx.jpg" 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%2Fpx992mkodop7tx2kuuxx.jpg" alt="A diagram representing a journey from Notification API to Native Apps" width="742" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although the MainActivity class code given in this post is Android platform-specific, the Firebase Cloud Messaging Service and the cross-platform services executed from the MainActivity can work nicely with an iOS app implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To receive push notifications on your app you need to:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ask the user’s permission for the app to display notifications&lt;/li&gt;
&lt;li&gt;register the user’s device with the Firebase project&lt;/li&gt;
&lt;li&gt;handle the receiving of the notification&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this post, let’s discuss the last part - receiving the notification. Our app loads a single-page application that routes to different pages, so let’s focus on a notification that contains a link to one of the app’s pages. These examples could also be applied to any other data type that each app would need the data object of the Firebase message to contain.&lt;/p&gt;

&lt;p&gt;Let’s say we want to send a notification with these JSON values to the device where the app is deployed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"notification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You received a message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Click to view your in-app messages."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://my-app/messages"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ultimately, we want the act of clicking the notification in the notifications tray to open the app, and if the Firebase message contains some extra data (like a URL), to pass along that data through an Intent object. Then the Android app should pick the data up and do something with it.&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%2Fntcbpf6z6a71rnxipsko.jpg" 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%2Fntcbpf6z6a71rnxipsko.jpg" alt="A diagram representing the journey from Firebase Cloud Messaging to Android App" width="752" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What is an Intent?
&lt;/h3&gt;

&lt;p&gt;In Android, an &lt;code&gt;Intent&lt;/code&gt; is a messaging object used to request an action from another app component and share some information. &lt;/p&gt;

&lt;p&gt;For example, the &lt;code&gt;Intent&lt;/code&gt; object is created by the Firebase Cloud Messaging service when a user clicks on a notification. The &lt;code&gt;Intent&lt;/code&gt; contains the data from the notification and is passed to the &lt;code&gt;OnCreate&lt;/code&gt; method of the app activity that is launched by the notification. Our code implementation decides what we do with that &lt;code&gt;Intent&lt;/code&gt; object. We might want to ignore it or, if the data in the &lt;code&gt;Intent&lt;/code&gt; makes sense, we can execute some action. In our case, if the &lt;code&gt;Intent&lt;/code&gt; object contains a URL field, we will try to use that URL to direct the user to the correct page in our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;We've learned that an Android app can operate in three different states: closed, in the background, or in the foreground. In each scenario, the push notification intent might need to be handled differently, especially if it contains a URL field for opening a specific page within the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Closed App
&lt;/h3&gt;

&lt;p&gt;We found that receiving and handling a notification from Firebase was very easy when the app is closed. The Firebase SDK would handle the receipt of the message, create a notification, and display it for the user. Once the user clicks the notification, the default activity will be started. In your activity class, the &lt;code&gt;OnCreate&lt;/code&gt; method will be executed. At this stage, we can get the URL from the &lt;code&gt;Intent&lt;/code&gt; object that was created by FCM, and then we use it to redirect the user to a specific screen in the app.&lt;/p&gt;

&lt;p&gt;The MainActivity class in Android is the entry point for your application. It is a subclass of the &lt;a href="https://developer.android.com/guide/components/activities/intro-activities" rel="noopener noreferrer"&gt;Activity class&lt;/a&gt;, which is a crucial component of an Android application. An activity represents a single screen with a user interface.&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="nf"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Theme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"@style/Maui.SplashTheme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="n"&gt;MainLauncher&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;LaunchMode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LaunchMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SingleTop&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;MainActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MauiAppCompatActivity&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bundle&lt;/span&gt; &lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;HandleIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Intent&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;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;HandleIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt; &lt;span class="n"&gt;intent&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;intent&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="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&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;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extras&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"url"&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&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;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UriKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RelativeOrAbsolute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Maui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Controls&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;Current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SendOnAppLinkRequestReceived&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="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 this example of the &lt;code&gt;MainActivity&lt;/code&gt; class, we create a Uri object and pass it to the &lt;code&gt;SendOnAppLinkRequestReceived&lt;/code&gt; method defined in &lt;code&gt;App.xaml.cs&lt;/code&gt; file. This is great because the App class contains cross-platform code. So if you are also implementing an iOS notification URL handling, your iOS-specific code can call the same cross-platform method in this App class and carry on with the same redirection behavior.&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;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&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;App&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="nf"&gt;InitializeComponent&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="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnAppLinkRequestReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnAppLinkRequestReceived&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;currentPage&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="nf"&gt;GetCurrentPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HandleDeepLinkRequest&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="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;Each page might do something different with this URI (sometimes redirect, sometimes ignore). That’s why we chose to implement and call a method for handling the URI in each page separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  App Running in the Background
&lt;/h3&gt;

&lt;p&gt;When the app is running in the background (minimized), Firebase again handles the notification creation and display. When the user clicks on this notification, the app will be foregrounded, but the &lt;code&gt;OnCreate&lt;/code&gt; method will not be called as the activity is already running. Instead, we need to override another method - &lt;code&gt;OnNewIntent()&lt;/code&gt;. This will be automatically called when the user clicks on the notification. In this method, we can get the URL from the &lt;code&gt;intent&lt;/code&gt; parameter and redirect.&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="nf"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Theme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"@style/Maui.SplashTheme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="n"&gt;MainLauncher&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;LaunchMode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LaunchMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SingleTop&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;MainActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MauiAppCompatActivity&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnNewIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnNewIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;HandleIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&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;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;HandleIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt; &lt;span class="n"&gt;intent&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;intent&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="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&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;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extras&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"url"&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&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;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UriKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RelativeOrAbsolute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Maui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Controls&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;Current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;SendOnAppLinkRequestReceived&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="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;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Note on the Launch mode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In .NET MAUI, the &lt;code&gt;LaunchMode = LaunchMode.SingleTop&lt;/code&gt; activity configuration is used to control the behavior of your app's activity in response to push notifications or other intents. This launch mode ensures that if an instance of the activity already exists at the top of the activity stack, it will be reused instead of creating a new instance. This is exactly the scenario we want to have when the app is running in the background.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  App Running in the Foreground
&lt;/h3&gt;

&lt;p&gt;When the app is open and a Firebase message is sent, the Firebase SDK doesn’t create and display the notification for us. We have to do this ourselves. For this reason, we implemented a messaging service extending the &lt;a href="https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService" rel="noopener noreferrer"&gt;&lt;code&gt;FirebaseMessagingService&lt;/code&gt;&lt;/a&gt; class. This is where the &lt;code&gt;OnMessageReceived&lt;/code&gt; method is automatically executed once the message is sent.  By overriding this method, we were able to take the data from the remote message, build a notification ourselves, including the intent with the URL, and display it for the user in the device’s notification tray.&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;_Microsoft.Android.Resource.Designer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Android.App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Android.Content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Android.OS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AndroidX.Core.App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AndroidX.Core.Content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Firebase.Messaging&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.Platforms.Notifications&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exported&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;IntentFilter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;"com.google.firebase.MESSAGING_EVENT"&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;NotificationMessagingService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FirebaseMessagingService&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;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;NotificationUrlDataKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"url"&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;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;NotificationChannelId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"someChannelId"&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;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnMessageReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemoteMessage&lt;/span&gt; &lt;span class="n"&gt;message&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;notification&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="nf"&gt;GetNotification&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;notificationId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;GetHashCode&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;pendingIntent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;BuildIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notificationId&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="n"&gt;Data&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;builtNotification&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;BuildNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pendingIntent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;CreateNotificationChannel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;SendNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notificationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;builtNotification&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;PendingIntent&lt;/span&gt; &lt;span class="nf"&gt;BuildIntent&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;notificationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IDictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;intent&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;Intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MainActivity&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ActivityFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SingleTop&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NotificationUrlDataKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PutExtra&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NotificationUrlDataKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&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;PendingIntent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;notificationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;PendingIntentFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OneShot&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;PendingIntentFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Immutable&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;Notification&lt;/span&gt; &lt;span class="nf"&gt;BuildNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PendingIntent&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RemoteMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;notificationBuilder&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;NotificationCompat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NotificationChannelId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;notificationBuilder&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetContentTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetContentText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetContentIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetAutoCancel&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;notificationBuilder&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;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CreateNotificationChannel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;channel&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;NotificationChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;NotificationChannelId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"MyApp Notifications"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;NotificationImportance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&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;notificationManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetSystemService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;NotificationManager&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;notificationManager&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;CreateNotificationChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&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;SendNotification&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;notificationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&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;notificationManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NotificationManagerCompat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;notificationManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notificationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification&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;This way, whenever a remote message is received from Firebase, the notification will be created and immediately displayed for the user. Once it’s clicked, &lt;code&gt;OnNewIntent&lt;/code&gt; method in the activity class will be executed, and the URL is going to be used for redirection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some readings ✨
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)" rel="noopener noreferrer"&gt;Android Activity&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/mohitrajput987/launch-modes-of-android-activity-59eo"&gt;Launch Modes of Android Activity&lt;/a&gt;&lt;br&gt;
&lt;a href="https://firebase.google.com/docs/cloud-messaging/android/receive" rel="noopener noreferrer"&gt;Receive Messages in an Android App | Firebase Cloud Messaging&lt;/a&gt;&lt;br&gt;
&lt;a href="https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService" rel="noopener noreferrer"&gt;FirebaseMessagingService class&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>firebase</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
