<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Code with Tuyen]]></title><description><![CDATA[C#.NET | Xamarin.Forms | MAUI | Flutter | ReactJS | ReactNative | ABP.io]]></description><link>https://tuyen-vuduc.tech</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 05:34:15 GMT</lastBuildDate><atom:link href="https://tuyen-vuduc.tech/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Artifact/NuGet metadata in dotnet-binding-util]]></title><description><![CDATA[In the previous post of Introduction of dotnet-binding-util, we already learned what it is about. I will sometimes call it the tool for short.
In the post, I will go thorough the core part of the tool, artifact and NuGet metadata, the way in which a ...]]></description><link>https://tuyen-vuduc.tech/artifact-nuget-metadata-in-dotnet-binding-util</link><guid isPermaLink="true">https://tuyen-vuduc.tech/artifact-nuget-metadata-in-dotnet-binding-util</guid><category><![CDATA[dotnet]]></category><category><![CDATA[#dotnet-maui]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Wed, 18 Dec 2024 04:06:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1734494699759/c599bcbb-9b52-448e-8024-299c57eedfa4.avif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous post of <a target="_blank" href="https://tuyen-vuduc.tech/introduction-of-dotnet-binding-util">Introduction of dotnet-binding-util</a>, we already learned what it is about. I will sometimes call it the tool for short.</p>
<p>In the post, I will go thorough the core part of the tool, <strong>artifact and NuGet metadata</strong>, the way in which a NuGet package links to a native Android artifact.</p>
<h2 id="heading-where-they-are">Where they are</h2>
<p>When checking out the tool, you will find there is a folder of android following the below structure.</p>
<pre><code class="lang-plaintext">|- path-to-the-tool
    |- src
        |- android
            |- android.arch.core
            |- android.arch.lifecycle
</code></pre>
<p><strong>NOTE:</strong> The idea repo of the tool will be for both the iOS and Android binding libraries. Hence, there are two folders: one for android, one for ios.</p>
<p>In this android folder, each artifact group will have its own folder. Inside an artifact group folder, there will be</p>
<ul>
<li><p><code>group.json</code> - Optional. Defines the info of the group such as name, dotnet8, etc.</p>
</li>
<li><p><code>mavens.props</code> - Optional. Defines custom Maven repositories.</p>
</li>
<li><p><code>icon.png</code> - Optional. The icon of the nested generated NuGet packages</p>
</li>
<li><p>nested folders - Each folder is a nested artifact within the group.</p>
</li>
</ul>
<p>Inside an artifact folder, we will have</p>
<ul>
<li><p><code>nuget.json</code> - Required. Defines the info of the associated NuGet package</p>
<ul>
<li><p><code>packageId</code> - Required. It’s the package ID of associated NuGet package. If not present, it will be the Pascal version of artifact group and artifact names combined.</p>
</li>
<li><p><code>name</code> - Optional. Normally it’s the name of the artifact. We can make it human friendly and SEO friendly.</p>
</li>
<li><p><code>dependencyOnly</code> - Optional. Default to <code>true</code>. Marks that no binding should be generated.</p>
</li>
<li><p><code>versionMapping</code> - Optional. This one is complex and complicated. It’s currently in use for Google related packages only.</p>
</li>
</ul>
</li>
<li><p><code>x.y.z.p.json</code> files</p>
<ul>
<li><p>The file name - The version of the native artifact</p>
</li>
<li><p>The file content:</p>
<ul>
<li><p><code>revision</code>: Optional. The revision of the Nuget package</p>
</li>
<li><p><code>fallbackVersion</code>: Optional. The version which we should fallback to and this version actually has no associated NuGet package</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><code>x.y.z.p.fixed.json</code> files: Contains the fixed versions of the dependencies of the artifact when creating the binding library. We will learn more details about it in a concrete example.</p>
</li>
</ul>
<p>For all of that long explanation, the main point is to define the way an Android artifact linked to a NuGet package.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734492223550/981e4db5-1728-4929-a48a-3a42bc2c2e74.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-how-to-update-the-existing-metadata">How to update the existing metadata</h2>
<p>These metadata is continuously updated by the NuGet packages’ owners. Hence, we will have to keep them up to date right before we want to do any new binding libraries or upgrade existing ones.</p>
<p>This update task is done by running this command in the BASH terminal</p>
<pre><code class="lang-bash">sh fetch.sh
</code></pre>
<p>This will check all existing metadata in our folder and check with NuGet.org to find any new versions added and/or modified with new revisions.</p>
<p>After the command is done, we must commit the change back to the tool for later reference.</p>
<h2 id="heading-how-to-add-metadata-for-an-android-binding-nuget-package">How to add metadata for an Android binding NuGet package</h2>
<p>Here are steps to add metadata for an Android binding NuGet package:</p>
<ul>
<li><p>Find the artifact group and artifact name of the native library</p>
</li>
<li><p>Create folders for artifact group and artifact</p>
</li>
<li><p>Create <code>nuget.json</code> file</p>
<ul>
<li><code>packageId</code>: The Nuget package id.</li>
</ul>
</li>
<li><p>Run <code>sh fetch.sh</code></p>
</li>
<li><p>Wait and see the result</p>
</li>
<li><p>Commit the new metadata</p>
</li>
</ul>
<p>Example of adding metadata for <a target="_blank" href="https://www.nuget.org/packages/Xamarin.RevenueCat.Android">Xamarin.RevenueCat.Android</a> which has the native library is <a target="_blank" href="https://mvnrepository.com/artifact/com.revenuecat.purchases/purchases">com.revenuecat.purchases:purchases</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734494334819/a0c19a5e-4450-4953-8496-b4461b65be0f.png" alt class="image--center mx-auto" /></p>
<p>The result after running the fetch.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734494519148/fa240f5f-8331-4816-92ee-711219e24011.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-wraps-up">Wraps up</h2>
<p>This is the basic info of the metadata, the core part of the tool. We will need to learn a bit further for special cases as we create binding libraries using the tool.</p>
<p>Happy binding!!!</p>
]]></content:encoded></item><item><title><![CDATA[Introduction of dotnet-binding-util]]></title><description><![CDATA[You might have created a .NET binding library by yourself by following the guide Binding a Java library from Microsoft docs. In many cases, it’s quite simple and straight forward for small libraries. However, there few drawbacks:

1/ You normally emb...]]></description><link>https://tuyen-vuduc.tech/introduction-of-dotnet-binding-util</link><guid isPermaLink="true">https://tuyen-vuduc.tech/introduction-of-dotnet-binding-util</guid><category><![CDATA[android-binding-library]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[android app development]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Mon, 11 Nov 2024 01:06:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730418734593/e6543818-163d-479d-b9a9-66ed46a8aab4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You might have created a .NET binding library by yourself by following the guide <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/android/binding-libs/binding-java-libs/binding-java-library?toc=%2Fdotnet%2Fmaui%2Ftoc.json&amp;bc=%2Fdotnet%2Fmaui%2Fbreadcrumb%2Ftoc.json&amp;view=net-maui-8.0">Binding a Java library</a> from Microsoft docs. In many cases, it’s quite simple and straight forward for small libraries. However, there few drawbacks:</p>
<ul>
<li><p>1/ You normally embed the native libraries artifacts inside your DLL</p>
<ul>
<li><p>1.a/ This makes your DLL is potentially very large</p>
</li>
<li><p>1.b/ It might causes conflicts if two different Nuget packages embed the same libraries</p>
</li>
<li><p>1.c/ We cannot utilize if the native libraries are already downloaded by Android Studio or other Android development tools.</p>
</li>
</ul>
</li>
<li><p>2/ You might try out <a target="_blank" href="https://www.nuget.org/packages/Xamarin.Build.Download">Xamarin.Build.Download</a> to download the libraries for you</p>
<ul>
<li><p>The good point: We can get rid of item 1.a</p>
</li>
<li><p>Items 1.b &amp; 1.c still persist</p>
</li>
</ul>
</li>
</ul>
<p>Other than that, a library normally doesn’t come without dependencies, we will have to analyze ourselves to find out</p>
<ul>
<li><p>if there is a NuGet available out there</p>
</li>
<li><p>if not, we will need to create the binding ourselves</p>
</li>
</ul>
<p>The things even get more headache when the libraries are privately distributed and protected with credentials on Gradle repositories.</p>
<p>By creating a lot of binding libraries (many are very big ones like Mapbox, Stripe, etc), I found the way that we can</p>
<ul>
<li><p>Utilize Gradle - the native tool to manage Android/Java libraries either without credentials or with credentials, to download the libraries for us and a.</p>
</li>
<li><p>Find out the dependencies easily by reading POM files</p>
</li>
<li><p>Find out which dependencies are already bound with public NuGet packages</p>
</li>
<li><p>Upgrade version easily</p>
</li>
</ul>
<p>In this series of Creating binding libraries for .NET Android, I will go through things step by step of how to use my approach for your own binding libraries based on my repository <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-binding-utils">tuyen-vuduc/dotnet-binding-utils</a>. At the end, you will get NuGet package(s) to use in your .NET Android app.</p>
<h2 id="heading-what-do-we-have-in-this-repository">What do we have in this repository</h2>
<p>1/ It is a toolset to create an .NET Android(Java) binding library</p>
<ul>
<li><p>It will analyze all dependencies of the targeting binding library by reading POM files downloaded by Gradle</p>
</li>
<li><p>It will generate .NET Android (Java) binding library project(s) with appropriate dependencies</p>
</li>
<li><p>It will build and pack the final result in NuGet packages</p>
</li>
</ul>
<p>2/ It manages metadata of many .NET Android(Java) binding libraries</p>
<ul>
<li>Any Android (Java) binding libraries available on Nuget.org can have its metadata defined in this repo</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730421490885/9137d26b-4efd-4dad-851f-ad2a7768f0ed.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p>Based on available metadata, it will link the corresponding NuGet packages of the dependencies to the generated CSPROJ file</p>
<ul>
<li><p>If a NuGet is available without its metadata,</p>
<ul>
<li><p>1/ We need to add a new one similar to existing ones</p>
</li>
<li><p>2/ Then run the tool again</p>
</li>
</ul>
</li>
<li><p>If not available, then a binding library project will be generated</p>
</li>
</ul>
</li>
</ul>
<p>3/ It has several scripts to simplify binding error fixes</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// process_field(); // generate metadata to change fields’ managed names to avoid name conflicts.</span>
<span class="hljs-comment">// process_IJsonDeserializer(); // genereate xpath for classes inherits from IJsonDeserializer</span>
<span class="hljs-comment">// process_Com_Example_Dsroom_Dao_IBaseDao();</span>
<span class="hljs-comment">// process_Com_Google_Android_Material_Circularreveal_ICircularRevealWidget();</span>
<span class="hljs-comment">// process_Android_Util_ITypeEvaluator();</span>
<span class="hljs-comment">// process_Android_Util_Property();</span>
<span class="hljs-comment">// process_downgrade();</span>
</code></pre>
<h2 id="heading-here-are-typical-steps-to-create-a-new-binding-library">Here are typical steps to create a new binding library</h2>
<p>1/ Fork my repository</p>
<p>2/ Open Git Bash on Windows or Terminal on other systems</p>
<p>3/ Ensure the existing metadata up to date by running <code>sh fetch.sh</code></p>
<p>4/ Find the library Gradle details</p>
<ul>
<li><p>The Gradle implementation of the library e.g. <code>de.hdodenhof:circleimageview:3.1.0</code></p>
</li>
<li><p>The Gradle repository details</p>
<ul>
<li><p>URL</p>
</li>
<li><p>Credentials</p>
</li>
</ul>
</li>
<li><p>By default, only these public repositories are supported</p>
<ul>
<li><p>Maven Central</p>
</li>
<li><p>Google</p>
</li>
<li><p>JCenter</p>
</li>
</ul>
</li>
</ul>
<p>5/ Create the binding libraries by running <code>sh bind.sh —artifact {YOUR_BINDIDNG_LIBRARY_IMPLEMETNATION_SYNTAX_IN_GRADLE}</code></p>
<p>6/ Fix all binding errors as reported then do the step 5 again until no more errors reported</p>
<p>7/ Create a PR to merge your changes to the main repository</p>
<p>If you’re lucky, you won’t have to do <strong>step 6</strong> where no errors reported, there will be NuGet packages generated in <code>nugets</code> folder. Otherwise, you need to be patient and careful to fix all reported errors then do <strong>step 7</strong> as the final step.</p>
<p>With generated NuGet packages, you can consume them locally or distribute wherever you prefer.</p>
<h2 id="heading-lets-create-a-binding-library">Let’s create a binding library</h2>
<p>In this simple demonstration, let’s create the binding library for <code>de.hdodenhof:circleimageview:3.1.0</code> as mentioned the guide from Microsoft referred early in this post.</p>
<h3 id="heading-1-fork-the-library">1/ Fork the library</h3>
<p>Please do the fork then clone to your local machine if you haven’t.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731282927431/9ff99164-be56-413e-b622-a400675f6402.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-2-open-bash-based-terminal">2/ Open bash based terminal</h3>
<ul>
<li><p>Open the repository in Visual Studio Code</p>
</li>
<li><p>Open bash based terminal panel as well</p>
<ul>
<li>On Windows, I normally use Git Bash which is installed along with Git for Windows.</li>
</ul>
</li>
</ul>
<h3 id="heading-3-ensure-the-metadata-up-to-date">3/ Ensure the metadata up-to-date</h3>
<p>I created a simple XUnit test to fetch the metadata of the existing libraries defined in the repository easily.</p>
<p>To invoke the update process, simply run <code>sh fetch.sh</code> on your preferred bash based terminal.</p>
<blockquote>
<p>On Windows, I recommend use Git Bash which comes along when you install Git for Windows.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731283690433/9239d866-9ba6-4a0b-84b3-d55145a1e145.png" alt class="image--center mx-auto" /></p>
<p>As you can see, after running the script, there are many libraries updated either a new patch or even a new version.</p>
<p>Commit these changes before moving onward.</p>
<h3 id="heading-4-find-the-librarys-gradle-details">4/ Find the library’s Gradle details</h3>
<p>After searching <code>circleimageview</code> on Google Search, we can see its GitHub repository</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731284265230/ace54620-b5fc-4497-afdf-b580ce92bc47.png" alt class="image--center mx-auto" /></p>
<p>By checking out the README, we can easily find out the Gradle implementation of the library.</p>
<pre><code class="lang-kotlin">dependencies {
    ...
    implementation <span class="hljs-string">'de.hdodenhof:circleimageview:3.1.0'</span>
}
</code></pre>
<p>What we need is the value of <code>de.hdodenhof:circleimageview:3.1.0</code></p>
<blockquote>
<p>NOTE: Android/Libraries are normally distributed on <a target="_blank" href="https://mvnrepository.com/">Maven Central repository</a> which is similar to NuGet.org in .NET world. You can search for the libraries there as well to easily get their latest versions if not presented in the README.</p>
</blockquote>
<h3 id="heading-5-create-the-binding-library">5/ Create the binding library</h3>
<p>Creating a binding library normally requires many steps, but I created a script, <code>bind.sh</code>, to make our life easier. Executing it will</p>
<ul>
<li><p>Create the binding library project for the targeted library</p>
</li>
<li><p>Add NuGet packages for all dependencies having corresponding metadata</p>
</li>
<li><p>Add and link binding projects for dependencies without corresponding metadata</p>
</li>
</ul>
<p>The syntax is</p>
<pre><code class="lang-bash"><span class="hljs-comment"># sh bind.sh —artifact {ARTIFACT_GROUP}:{ARTIFACT_NAME}:{VERSION}</span>
sh bind.sh —artifact de.hdodenhof:circleimageview:3.1.0
</code></pre>
<p>Here is what you can see when executing <code>sh bind.sh —artifact de.hdodenhof:circleimageview:3.1.0</code></p>
<ul>
<li>It’s starting</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731285257676/6ec2105b-ada0-4949-8ceb-3ed8755cb3b8.png" alt class="image--center mx-auto" /></p>
<ul>
<li>It’s successfully without any binding errors</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731285427342/c0eef56a-6eb6-49ca-afbb-b12b34701375.png" alt class="image--center mx-auto" /></p>
<p>As you can see on the right left hand side, a binding project is created with all required files which are for fixing binding errors.</p>
<h3 id="heading-6-fix-reported-binding-errors">6/ Fix reported binding errors</h3>
<p>As you can see, no errors are reported, then we can skip this step.</p>
<blockquote>
<p>The process of fixing binding errors sometimes is very simple, but sometimes is very complex; however, we need to always follow <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/android/binding-libs/customizing-bindings/">the guide from Microsoft</a> as the base line.</p>
</blockquote>
<h3 id="heading-7-create-the-pr-to-merge-your-new-binding-library-to-my-repository">7/ Create the PR to merge your new binding library to my repository</h3>
<p>Commit all the changes then create the PR.</p>
<blockquote>
<p>Please follow <a target="_blank" href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork">the guide from GitHub</a> to complete this step.</p>
</blockquote>
<p>We can skip if for now and check out the generated library packed as a NuGet library first.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731286201927/62719726-22c8-4fc7-81b6-0622b3d44403.png" alt class="image--center mx-auto" /></p>
<p>You can use it locally or upload it to NuGet.org then use. If you prefer a local try first, you can check out <a target="_blank" href="https://learn.microsoft.com/en-us/nuget/hosting-packages/local-feeds">this guide</a> if you don’t know yet.</p>
<h2 id="heading-wraps-up">Wraps up</h2>
<p>In this post, I introduced you my repository <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/android/binding-libs/binding-java-libs/binding-java-maven-library">dotnet-binding-utils</a> which is to utilize Gradle and other custom scripts to simplify the process of creating an Android binding library. The major benefits are</p>
<ul>
<li><p>Utilize the native Android package management to manage Android dependencies</p>
</li>
<li><p>Reduce diskspace if we work on both Android native development and .NET Android development</p>
</li>
<li><p>Simple process</p>
</li>
<li><p>Results consumed easily as NuGet packages</p>
</li>
</ul>
<p>I hope it will help and benefit you guys.</p>
<p>It’s just the start, there are many other scenarios, please check out my other related posts (upcoming).</p>
<p>Happy coding!!!</p>
<blockquote>
<p>NOTE: Microsoft also introduced <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/android/binding-libs/binding-java-libs/binding-java-maven-library">a new way of adding a native Android library</a> which is publicly available on Maven Central library. What I don’t like is it doesn’t utilize Gradle but <code>Xamarin.Build.Download</code> to download the dependency.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Introduce Stripe for .NET MAUI]]></title><description><![CDATA[Overview
Stripe is a very well known payment service to let your end users pay easily and beautifully for your product/service(s) within your mobile apps and websites.
This article will guide you steps to integrate Stripe SDK into a newly created .NE...]]></description><link>https://tuyen-vuduc.tech/introduce-stripe-for-net-maui</link><guid isPermaLink="true">https://tuyen-vuduc.tech/introduce-stripe-for-net-maui</guid><category><![CDATA[dotnet]]></category><category><![CDATA[#dotnet-maui]]></category><category><![CDATA[stripe]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Sun, 23 Jun 2024 17:35:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1719159113292/83202984-b10c-47bd-be93-d0cff71000a6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview">Overview</h2>
<p><a target="_blank" href="https://stripe.com">Stripe</a> is a very well known payment service to let your end users pay easily and beautifully for your product/service(s) within your mobile apps and websites.</p>
<p>This article will guide you steps to integrate Stripe SDK into a newly created .NET MAUI app step by step.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<ul>
<li><p>.NET 8</p>
</li>
<li><p>Java SDK 17</p>
</li>
<li><p>Visual Studio on Windows or Visual Studio for Mac, alternatively</p>
<ul>
<li><p>JetBrains Rider alternatively</p>
</li>
<li><p>Visual Studio Code with .NET MAUI extension</p>
</li>
</ul>
</li>
<li><p>.NET workloads for .NET MAUI</p>
</li>
<li><p>Download <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-stripe-quickstart/archive/refs/heads/feat/get-started-starting-point.zip">the get-started source code</a></p>
</li>
</ul>
<h2 id="heading-steps">Steps</h2>
<h3 id="heading-0-open-the-downloaded-solution-dotnet-maui-stripe-get-startedsln">0/ Open the downloaded solution <code>dotnet-maui-stripe-get-started.sln</code></h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719160657520/112a6f29-9a09-4586-a56a-e8727177fdf0.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-0a-create-shareddevcs-file-by-making-a-copy-of-shareddevcssample">0.a/ Create <code>Shared.dev.cs</code> file by making a copy of <code>Shared.dev.cs.sample</code></h4>
<pre><code class="lang-plaintext">|--src
    |-- QuickStart.Dotnet.StripeHost
        |-- Shared.cs
        |-- Shared.dev.cs        &lt;------      YOUR_FILE_HERE
</code></pre>
<h4 id="heading-0b-replace-placeholders-with-your-secrets">0.b/ Replace placeholders with your secrets</h4>
<pre><code class="lang-csharp"><span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ClientHelper</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> API_KEY = <span class="hljs-string">"YOUR_API_KEY"</span>;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> DEFAULT_PUBLISHABLE_KEY = <span class="hljs-string">"YOUR_DEFAULT_PUBLISHABLE_KEY"</span>;
}
</code></pre>
<blockquote>
<p>HINTS: You can copy these secrets quickly from <a target="_blank" href="https://docs.stripe.com/payments/quickstart?platform=ios">the official quick start by Stripe</a>.</p>
</blockquote>
<h4 id="heading-0c-run-the-api-host">0.c/ Run the API host</h4>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> your-path-to-source-code/src/QuickStart.Dotnet.StripeHost
dotnet run -c debug
</code></pre>
<h3 id="heading-0d-expose-your-api-endpoint-with-ngrokhttpsngrokcom">0.d/ Expose your API endpoint with <a target="_blank" href="https://ngrok.com">ngrok</a></h3>
<pre><code class="lang-bash">ngrok http http://localhost:4242
</code></pre>
<blockquote>
<p>NOTE: <a target="_blank" href="https://ngrok.com">ngrok</a> is a tool that allows us to expose a development endpoint publicly via the network with ease.</p>
</blockquote>
<h4 id="heading-0e-amend-shareddevcs-with-the-generated-url">0.e/ Amend <code>Shared.dev.cs</code> with the generated URL</h4>
<pre><code class="lang-csharp"><span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ClientHelper</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> BACKEND_URL= <span class="hljs-string">"https://generated-subdomain.ngrok-free.app"</span>;
}
</code></pre>
<h3 id="heading-1-create-a-new-net-maui-app-from-visual-studio">1/ Create a new .NET MAUI app from Visual Studio</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719160709286/c53865d3-b740-4a43-87ef-ba06741b92d7.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>NOTE: Follow step by step with all default options</p>
</blockquote>
<h3 id="heading-2-install-stripe-sdk-for-net-maui">2/ Install Stripe SDK for .NET MAUI</h3>
<h4 id="heading-2a-upgrade-all-net-maui-packages-before-installing-stripemaui-package">2.a/ Upgrade all .NET MAUI packages before installing <code>Stripe.MAUI</code> package</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719160874147/55fab0bb-bb85-4589-bb13-8da79f87db96.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-2b-search-for-stripemaui-package-and-install">2.b/ Search for <code>Stripe.Maui</code> package and install</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719160957305/28861e3d-b2ec-4f7b-bd7e-d882f69cc197.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>NOTE:</p>
<ul>
<li><p>Before going next</p>
<ul>
<li><p>Close the solution</p>
</li>
<li><p>Remove bin/obj folders of the .NET MAUI project</p>
</li>
<li><p>Reopen the solution</p>
</li>
</ul>
</li>
<li><p><strong>IMPORTANT!!!</strong> If you faced any issues of project loading failed, please repeat these steps :D</p>
</li>
</ul>
</blockquote>
<h3 id="heading-3-enable-stripe-in-mauiprogramcs">3/ Enable Stripe in <code>MauiProgram.cs</code></h3>
<h4 id="heading-3a-add-shared-files-to-the-net-maui-project">3.a/ Add shared files to the .NET MAUI project</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719162229996/f102e634-a16c-40f1-bc0f-a30d3890ce8d.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>NOTE: We will add these files as links to share code across projects.</p>
</blockquote>
<h4 id="heading-3b-add-stripe-to-mauiappbuilder">3.b/ Add Stripe to <code>MauiAppBuilder</code></h4>
<pre><code class="lang-csharp">builder
    .UseStripe(
        QuickStart.Dotnet.Stripe.ClientHelper.DEFAULT_PUBLISHABLE_KEY
    );
</code></pre>
<h3 id="heading-4-inject-ipaymentsheet-into-mainpagexamlcs">4/ Inject <code>IPaymentSheet</code> into <code>MainPage.xaml.cs</code></h3>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MainPage</span> : <span class="hljs-title">ContentPage</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IPaymentSheet paymentSheet;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">MainPage</span>(<span class="hljs-params">IPaymentSheet paymentSheet</span>)</span>
    {
        InitializeComponent();
        <span class="hljs-keyword">this</span>.paymentSheet = paymentSheet;
    }
}
</code></pre>
<p><strong>NOTE:</strong> To get the DI to work, we need to register <code>MainPage</code> as a scoped dependency through <code>MauiAppBuilder</code>.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// In MauiProgram.cs</span>
builder.Services.AddScoped&lt;MainPage&gt;();
</code></pre>
<h3 id="heading-5-try-paying-with-payment-intent">5/ Try paying with payment intent</h3>
<p>5.a/ Override <code>MainPage.xaml</code> file with the below content</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8" ?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">ContentPage</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://schemas.microsoft.com/dotnet/2021/maui"</span>
             <span class="hljs-attr">xmlns:x</span>=<span class="hljs-string">"http://schemas.microsoft.com/winfx/2009/xaml"</span>
             <span class="hljs-attr">x:Class</span>=<span class="hljs-string">"GetStartedWithStripe.MainPage"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">StackLayout</span>
        <span class="hljs-attr">Padding</span>=<span class="hljs-string">"30,24"</span>
        <span class="hljs-attr">Spacing</span>=<span class="hljs-string">"25"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Button</span>
            <span class="hljs-attr">x:Name</span>=<span class="hljs-string">"PayNowButton"</span>
            <span class="hljs-attr">Text</span>=<span class="hljs-string">"Pay now"</span>
            <span class="hljs-attr">SemanticProperties.Hint</span>=<span class="hljs-string">"Pay with Stripe"</span>
            <span class="hljs-attr">Clicked</span>=<span class="hljs-string">"OnPayNow"</span>
            <span class="hljs-attr">IsEnabled</span>=<span class="hljs-string">"False"</span>
            <span class="hljs-attr">VerticalOptions</span>=<span class="hljs-string">"EndAndExpand"</span>
            <span class="hljs-attr">HorizontalOptions</span>=<span class="hljs-string">"Fill"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StackLayout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">ContentPage</span>&gt;</span>
</code></pre>
<h4 id="heading-5b-fetch-payment-intent-client-secret-from-the-above-api-endpoint">5.b/ Fetch payment intent client secret from the above API endpoint</h4>
<pre><code class="lang-csharp"><span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> paymentIntentClientSecret;
<span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnAppearing</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">base</span>.OnAppearing();

    <span class="hljs-comment">// The helper method comes from the downloaded source code</span>
    <span class="hljs-keyword">await</span> QuickStart.Dotnet.Stripe.ClientHelper.FetchPaymentIntent()
        .ContinueWith(t =&gt;
        {
            <span class="hljs-keyword">var</span> (ok, msg) = t.Result;
            <span class="hljs-keyword">if</span> (!ok)
            {
                MainThread.BeginInvokeOnMainThread(() =&gt; DisplayAlert(<span class="hljs-string">"API Error"</span>, msg, <span class="hljs-string">"OK"</span>));
                <span class="hljs-keyword">return</span>;
            }

            paymentIntentClientSecret = msg;

            MainThread.BeginInvokeOnMainThread(() =&gt; PayNowButton.IsEnabled = <span class="hljs-literal">true</span>);
        })
        .ConfigureAwait(<span class="hljs-literal">false</span>);
}
</code></pre>
<blockquote>
<p>NOTE:</p>
<ul>
<li><p>By default, <code>Pay now</code> button is disabled</p>
</li>
<li><p>It will only be enabled if we can fetch the client secret successfully from the API</p>
</li>
</ul>
</blockquote>
<h4 id="heading-5c-add-onpaynow-handler">5.c/ Add <code>OnPayNow</code> handler</h4>
<pre><code class="lang-csharp">    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnPayNow</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> sender, EventArgs e</span>)</span>
    {
        <span class="hljs-keyword">var</span> configuration = <span class="hljs-keyword">new</span> Configuration(<span class="hljs-string">"TuyenTV.dev Co.Ltd."</span>);

        <span class="hljs-keyword">var</span> (result, msg) = <span class="hljs-keyword">await</span> paymentSheet.PresentWithPaymentIntentAsync(paymentIntentClientSecret, configuration)
            .ContinueWith(t =&gt;
            {
                <span class="hljs-keyword">if</span> (t.IsCompletedSuccessfully) <span class="hljs-keyword">return</span> (t.Result, <span class="hljs-literal">null</span>);

                <span class="hljs-keyword">return</span> (PaymentSheetResult.Canceled, <span class="hljs-string">"Cannot pay for a reason..."</span>);
            });

        <span class="hljs-keyword">switch</span>(result)
        {
            <span class="hljs-keyword">case</span> PaymentSheetResult.Completed:
                <span class="hljs-keyword">await</span> DisplayAlert(<span class="hljs-string">"Payment Result"</span>, <span class="hljs-string">"Payment completed successfully!"</span>, <span class="hljs-string">"OK"</span>);
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> PaymentSheetResult.Canceled:
                <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">string</span>.IsNullOrWhiteSpace(msg))
                {
                    <span class="hljs-keyword">await</span> DisplayAlert(<span class="hljs-string">"Payment Result"</span>, <span class="hljs-string">"Payment completed failed!"</span>, <span class="hljs-string">"Try again"</span>);
                }
                <span class="hljs-keyword">break</span>;
        }

        Debug.WriteLine(<span class="hljs-string">"Payment complete."</span>);
    }
</code></pre>
<h3 id="heading-6-run-up-and-check-out">6/ Run up and check out</h3>
<p>Till now, you're ready to check out the result on both Android and iOS.</p>
<ul>
<li><p><strong>Android</strong>: You can try either on a emulator or on a real device by plugging it in directly to your PC</p>
</li>
<li><p><strong>iOS</strong>:</p>
<ul>
<li><p>If you have an Apple paid developer account, you can plug your iPhone directly to your PC and try out</p>
</li>
<li><p>Otherwise, you will need a MacOS device to run the simulator</p>
</li>
</ul>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719163925457/23a88aee-f1a6-4c47-a4d5-0711cf59b4ef.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-wrap-ups">Wrap-ups</h2>
<p>I just guided you on how to integrate Stripe SDK into a newly created .NET MAUI project. You can follow these steps to add it to your project(s) easily.</p>
<p><a target="_blank" href="https://docs.stripe.com">Documents from Stripe</a> are also very helpful, please always take a look to understand a feature you desire to add to your app.</p>
<p>If you want to check out the source code of the binding libraries, you can check it <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-binding-utils"><strong>here</strong></a>. If you want to check out the source code of the .NET MAUI library (along with a sample app), you can check it <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-maui-stripe"><strong>here</strong></a>.</p>
<p>Happy Coding!!!</p>
]]></content:encoded></item><item><title><![CDATA[Introduce ShakeBugs for .NET MAUI]]></title><description><![CDATA[Overview
As an mobile app developer, you might ever think if your users can be your app testers who can log an encountered bug with a lot of details for our triage and bug fixing like screenshot, app logs, etc. Sometime, your users also want to give ...]]></description><link>https://tuyen-vuduc.tech/introduce-shakebugs-for-net-maui</link><guid isPermaLink="true">https://tuyen-vuduc.tech/introduce-shakebugs-for-net-maui</guid><category><![CDATA[shakebugs]]></category><category><![CDATA[#dotnet-maui]]></category><category><![CDATA[dotnet]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Thu, 20 Jun 2024 00:13:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1718353943738/e3c9f67c-e5d0-41e0-b9fe-0202041b25d6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview">Overview</h2>
<p>As an mobile app developer, you might ever think if your users can be your app testers who can log an encountered bug with a lot of details for our triage and bug fixing like screenshot, app logs, etc. Sometime, your users also want to give an idea of improvement, but they can't help.</p>
<p>Understanding this paint point, there are many services out there to help us. <a target="_blank" href="https://www.shakebugs.com/">ShakeBugs</a> is one of them, an SaaS service for collecting and managing bugs and improvements from our app users in production.</p>
<p>This article will guide you how to integrate ShakeBugs SDK into your <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/maui">.NET MAUI</a> application step by step.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>.NET 8</p>
</li>
<li><p>Java SDK 17</p>
</li>
<li><p>Visual Studio on Windows or Visual Studio for Mac, alternatively</p>
<ul>
<li><p>JetBrains Rider alternatively</p>
</li>
<li><p>Visual Studio Code with .NET MAUI extension</p>
</li>
</ul>
</li>
<li><p>.NET workloads for .NET MAUI</p>
</li>
</ul>
<h2 id="heading-steps">Steps</h2>
<h3 id="heading-1-create-shakebugs-apps-for-ios-and-android">1/ Create ShakeBugs apps for iOS and Android</h3>
<p>Creating ShakeBugs apps is pretty easy, you can simply follow the steps as you go after logging in in <a target="_blank" href="https://app.shakebugs.com/">their portal</a>.</p>
<p>We need to create two apps</p>
<ul>
<li><p>One for Native Android</p>
</li>
<li><p>One for Native iOS</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1718354938253/db44f2b0-a659-4ce0-b628-f2001d1fc32c.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-2-create-a-new-net-maui-project">2/ Create a new .NET MAUI project</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1718355088094/a73207af-64c5-42b5-a5e7-4497aeaa0e56.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p><strong>NOTE:</strong> Please update all .NET MAUI packages to the latest version (8.0.60 at the time of this writing).</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1718355512124/23f2350b-9331-4698-b156-f8751a54347f.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>NOTE: As we only target Android and iOS platforms, so we should remove other platforms from our CSPROJ file.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1718355973500/fbb39fbc-da85-44f1-be5f-a21f570b6231.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-3-install-shakebugs-for-net-maui-nuget-package">3/ Install ShakeBugs for .NET MAUI NuGet package</h3>
<p>You can search for <code>ShakeBugs.Maui</code> from the NuGet package manager window.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1718355217420/c3ba418c-86a2-4185-af9a-7843e1aebfea.png" alt class="image--center mx-auto" /></p>
<p>Or you can simply open the CSPROJ file then add these lines</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"ShakeBugs.MAUI"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">Version</span>&gt;</span>17.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">Version</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">PackageReference</span>&gt;</span>
</code></pre>
<h3 id="heading-4-amend-mauiprogramcs-to-have-shakebugs-enabled">4/ Amend <code>MauiProgram.cs</code> to have ShakeBugs enabled</h3>
<pre><code class="lang-csharp">builder
    ...
    .UseShakeBugs(
        androidApiKey: <span class="hljs-string">"YOUR_ANDROID_API_KEY"</span>,
        iosApiKey: <span class="hljs-string">"YOUR_IOS_API_KEY"</span>,
        crashReportingEnabled: <span class="hljs-literal">true</span>
    )
    ...
</code></pre>
<h3 id="heading-5-voila-run-the-app-up-and-shake-your-device">5/ Voila! Run the app up and shake your device</h3>
<p>After you get above steps done, you are now ready to check out ShakeDebugs user interface with two steps</p>
<ul>
<li><p>1/ Run up the app</p>
</li>
<li><p>2/ Shake your device</p>
</li>
</ul>
<h2 id="heading-wrap-ups">Wrap ups</h2>
<p>I just guided you on how to integrate ShakeBugs SDK into a .NET MAUI project. You can follow the steps and add it to your project(s) easily.</p>
<p><a target="_blank" href="https://docs.shakebugs.com/docs/">Documents from ShakeBugs</a> are also very helpful, please take a look to understand all features it provides.</p>
<p>If you want to check out the source code of the binding libraries, you can check it <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-binding-utils">here</a>. If you want to check out the source code of the .NET MAUI library (along with a sample app), you can check it <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-shakebugs-maui">here</a>.</p>
<p>Happy coding!!!</p>
]]></content:encoded></item><item><title><![CDATA[Remove entry border for your .NET MAUI app]]></title><description><![CDATA[Today, I successfully upgraded my Chick and Paddy .NET MAUI sample to .NET8. It was a long wait because I failed to upgrade to .NET8 preview releases.
Things went well except the entry border which was supposed to be none in .NET6/7 as you might find...]]></description><link>https://tuyen-vuduc.tech/remove-entry-border-for-your-net-maui-app</link><guid isPermaLink="true">https://tuyen-vuduc.tech/remove-entry-border-for-your-net-maui-app</guid><category><![CDATA[dotnet]]></category><category><![CDATA[#dotnet-maui]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Fri, 24 Nov 2023 00:13:53 GMT</pubDate><content:encoded><![CDATA[<p>Today, I successfully upgraded <a target="_blank" href="https://github.com/tuyen-vuduc/chick-and-paddy-dotnet-maui">my Chick and Paddy .NET MAUI sample</a> to .NET8. It was a long wait because I failed to upgrade to .NET8 preview releases.</p>
<p>Things went well except the entry border which was supposed to be none in .NET6/7 as you might find the code snippet in <a target="_blank" href="https://dev.to/vhugogarcia/remove-entry-and-picker-borders-in-net-maui-2pk2">this article</a>.</p>
<p>After reading for a while, I found <a target="_blank" href="https://github.com/dotnet/maui/issues/7906#issuecomment-1712740984">the comment in a GitHub issue</a> and here is the way we need to remove the entry border.</p>
<pre><code class="lang-csharp">Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(<span class="hljs-string">"NoBorderEntry"</span>, (handler, view) =&gt;
{
<span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> ANDROID</span>
        handler.PlatformView.Background = <span class="hljs-literal">null</span>;
        handler.PlatformView.SetBackgroundColor(Android.Graphics.Color.Transparent);
        handler.PlatformView.BackgroundTintList = Android.Content.Res.ColorStateList.ValueOf(Android.Graphics.Color.Transparent);
<span class="hljs-meta">#<span class="hljs-meta-keyword">elif</span> IOS || MACCATALYST</span>
        handler.PlatformView.BackgroundColor = UIKit.UIColor.Clear;
        handler.PlatformView.Layer.BorderWidth = <span class="hljs-number">0</span>;
        handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
<span class="hljs-meta">#<span class="hljs-meta-keyword">elif</span> WINDOWS</span>
        handler.PlatformView.FontWeight = Microsoft.UI.Text.FontWeights.Thin;
<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span>
});
</code></pre>
<p>Happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[How to use Mapbox in your .NET MAUI app]]></title><description><![CDATA[Overview
Mapbox is a well-known service for maps and location and it provides its SDKs for multiple platforms from the web to the mobile. Within the mobile, there are prebuilt SDKs for ReactNative and Flutter, but none for Xamarin.Forms/.NET MAUI.
Re...]]></description><link>https://tuyen-vuduc.tech/how-to-use-mapbox-in-your-dotnet-maui-app</link><guid isPermaLink="true">https://tuyen-vuduc.tech/how-to-use-mapbox-in-your-dotnet-maui-app</guid><category><![CDATA[mapbox]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[#dotnet-maui]]></category><category><![CDATA[ios app development]]></category><category><![CDATA[android app development]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Tue, 18 Jul 2023 08:25:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1719535081021/7c0bfb69-1352-4e5d-a0b2-384e5feff156.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview">Overview</h2>
<p><a target="_blank" href="https://www.mapbox.com/">Mapbox</a> is a well-known service for maps and location and it provides its SDKs for multiple platforms from the web to the mobile. Within the mobile, there are prebuilt SDKs for ReactNative and Flutter, but none for Xamarin.Forms/.NET MAUI.</p>
<p>Recently, I successfully created and published <a target="_blank" href="https://www.nuget.org/packages/Mapbox.Maui/">the NuGet package</a> for integrating Mapbox SDKs to a Xamarin.Forms and/or .NET MAUI app.</p>
<pre><code class="lang-bash">dotnet add package Mapbox.Maui --version 11.4.0-alpha01
</code></pre>
<p>In this blog, I will guide you through the steps to use that mentioned NuGet package in a .NET MAUI app.</p>
<blockquote>
<p>TLTR; You can access the full quickstart example from <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-mapbox-quickstart">this GitHub repo</a>.</p>
</blockquote>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Visual Studio for Mac or Visual Studio on Windows (VS Code)</p>
</li>
<li><p>.NET 8.0.100</p>
</li>
<li><p>.NET workloads for Android, iOS, MAUI</p>
</li>
</ul>
<h2 id="heading-steps">Steps</h2>
<blockquote>
<p>Assumption: We will start with a totally new, empty .NET MAUI app.</p>
</blockquote>
<h3 id="heading-1-config-your-mapboxdownloadstoken">1/ Config your <code>MAPBOX_DOWNLOADS_TOKEN</code></h3>
<p>At the moment, this setup for iOS and Android is a little bit different, so we have to do it twice, differently for each platform. You can read my blog posts <a target="_blank" href="https://tuyen-vuduc.tech/how-to-use-mapbox-for-your-dotnet-android-app">for .NET Android</a> and <a target="_blank" href="https://tuyen-vuduc.tech/how-to-use-mapbox-in-your-net-ios-app">for .NET iOS</a> for further details.</p>
<ul>
<li><p>Grab your MAPBOX_DOWNLOADS_TOKEN from <a target="_blank" href="https://account.mapbox.com/">your Mapbox account</a> page</p>
</li>
<li><p>Put that token <code>~/.gradle/gradle.properties</code> from the terminal (Replace YOUR_MAPBOX_DOWNLOADS_TOKEN with yours)</p>
</li>
</ul>
<pre><code class="lang-xml">echo "MAPBOX_DOWNLOADS_TOKEN=YOUR_MAPBOX_DOWNLOADS_TOKEN" &gt;&gt; ~/.gradle/gradle.properties
</code></pre>
<ul>
<li>Amend your <code>.csproj</code> file with the following lines (Replace YOUR_MAPBOX_DOWNLOADS_TOKEN with yours)</li>
</ul>
<pre><code class="lang-csharp">&lt;PropertyGroup&gt;
    &lt;MAPBOX_DOWNLOADS_TOKEN&gt;YOUR_MAPBOX_DOWNLOADS_TOKEN&lt;/MAPBOX_DOWNLOADS_TOKEN&gt;
&lt;/PropertyGroup&gt;
&lt;ItemGroup&gt;
    &lt;GradleRepository Include=<span class="hljs-string">"https://api.mapbox.com/downloads/v2/releases/maven"</span>&gt;
      &lt;Repository&gt;
      maven {
         url = uri(<span class="hljs-string">"https://api.mapbox.com/downloads/v2/releases/maven"</span>)
         authentication {
             create&amp;lt;BasicAuthentication&amp;gt;(<span class="hljs-string">"basic"</span>)
         }
         credentials {
             <span class="hljs-comment">// Do not change the username below.</span>
             <span class="hljs-comment">// This should always be `mapbox` (not your username).</span>
             username = <span class="hljs-string">"mapbox"</span>
             <span class="hljs-comment">// Use the secret token you stored in gradle.properties as the password</span>
             password = providers.gradleProperty(<span class="hljs-string">"MAPBOX_DOWNLOADS_TOKEN"</span>).<span class="hljs-keyword">get</span>()
         }
      }
      &lt;/Repository&gt;
    &lt;/GradleRepository&gt;
&lt;/ItemGroup&gt;
</code></pre>
<blockquote>
<p><strong><em>NOTE:</em></strong><code>MAPBOX_DOWNLOADS_TOKEN</code><strong><em>is a sensitive data item, it shouldn't be committed to our git repository. We should put it in an ignored file, but still understandable by the compiler.</em></strong></p>
</blockquote>
<h3 id="heading-2-add-mapbox-maui-for-net-to-your-project">2/ Add <code>Mapbox MAUI for .NET</code> to your project</h3>
<ul>
<li><p>Open NuGet Package Manager</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689663257376/077015ea-1c75-4711-87c0-049d1dc3d525.png" alt /></p>
</li>
<li><p>Search for the package <code>Mapbox.Maui</code></p>
</li>
<li><p>Add it to your project</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689664059675/7ad2d37a-1db8-4ead-87f3-c47187a8c6eb.png" alt /></p>
</li>
<li><p>Check the result</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689664102834/854819e3-0876-46b3-aa80-884bbd2c51bc.png" alt /></p>
</li>
<li><p>Try to build the project</p>
<blockquote>
<p>A successful build means you did it well by far</p>
</blockquote>
</li>
</ul>
<h3 id="heading-4-configure-mapboxaccesstoken">4/ Configure <code>mapbox_access_token</code></h3>
<p>To work with Mapbox APIs, we have to generate a personalized access token from <a target="_blank" href="https://account.mapbox.com/">our Mapbox account page</a> and then put it into our project to use</p>
<ul>
<li><p>Generate/grab the access token</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689629809872/bee32c30-7025-4533-b653-eb5f227dbb59.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>You can use <em>the default public token</em> for this tutorial. If you want more control over what scopes to be used, please create a new token</p>
</blockquote>
</li>
<li><p>Amend <code>MauiProgram.cs</code> as the below code snippet</p>
<pre><code class="lang-csharp">  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MauiProgram</span>
  {
      <span class="hljs-comment">// Please put your MAPBOX_ACCESS_TOKEN here</span>
      <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> ACCESS_TOKEN = <span class="hljs-string">"YOUR_MAPBOX_ACCESS_TOKEN"</span>;

      <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> MauiApp <span class="hljs-title">CreateMauiApp</span>(<span class="hljs-params"></span>)</span>
      {
          <span class="hljs-comment">// ...</span>
          builder
              <span class="hljs-comment">// ...</span>
              .UseMapbox(ACCESS_TOKEN);
          <span class="hljs-comment">// ...</span>
</code></pre>
<blockquote>
<p>NOTE: <code>mapbox_access_token</code> is a sensitive data item, it shouldn't be committed to our git repository. We should put it in an ignored file, but still understandable by the compiler.</p>
</blockquote>
</li>
</ul>
<h3 id="heading-5-add-mapview-control-to-your-app">5/ Add <code>MapView</code> control to your app</h3>
<ul>
<li>In XAML <code>MainPage.xaml</code></li>
</ul>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8" ?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">ContentPage</span>
    <span class="hljs-attr">...</span>
    <span class="hljs-attr">xmlns:mb</span>=<span class="hljs-string">"clr-namespace:MapboxMaui;assembly=Mapbox.Maui"</span>
    <span class="hljs-attr">...</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">mb:MapboxView</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">ContentPage</span>&gt;</span>
</code></pre>
<ul>
<li>Remove all old logic in <code>MainPage.xaml.cs</code></li>
</ul>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MainPage</span> : <span class="hljs-title">ContentPage</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">MainPage</span>(<span class="hljs-params"></span>)</span>
    {
        InitializeComponent();
    }
}
</code></pre>
<ul>
<li>Check out the result</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689668550455/a54ac427-939e-470c-884a-218e85cd5215.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p><strong>NOTE</strong>: You can also initialize <code>MapView</code> instance directly from C#.</p>
</blockquote>
<h2 id="heading-wrap-up">Wrap up</h2>
<p>I just guided you on how to use Mapbox in a .NET MAUI project.</p>
<p>I already ported 10 out of 66 iOS examples to .NET MAUI. You can check out <a target="_blank" href="https://github.com/tuyen-vuduc/mapbox-maui">Mapbox SDK for .NET MAUI repository</a>.</p>
<p>I want to port all the rest to .NET MAUI, but it'll be a time-consuming task. Single me will last for a very long time. I hope the community can contribute to <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-mapbox-quickstart">this GitHub repo</a> and/or to their repos.</p>
]]></content:encoded></item><item><title><![CDATA[How to use Mapbox in your .NET iOS app]]></title><description><![CDATA[Overview
Mapbox is a well-known service for maps and location and it provides its SDKs for multiple platforms from the web to the mobile. Within the mobile, there are SDKs for Android, iOS, ReactNative and Flutter, but none for Xamarin/.NET iOS.
Rece...]]></description><link>https://tuyen-vuduc.tech/how-to-use-mapbox-in-your-net-ios-app</link><guid isPermaLink="true">https://tuyen-vuduc.tech/how-to-use-mapbox-in-your-net-ios-app</guid><category><![CDATA[dotnet]]></category><category><![CDATA[ios app development]]></category><category><![CDATA[mapbox]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Tue, 18 Jul 2023 05:13:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1719535352764/8ed43c15-f23a-4b3c-aee4-20c5dd282697.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview"><strong>Overview</strong></h2>
<p><a target="_blank" href="https://www.mapbox.com/"><strong>Mapbox</strong></a> is a well-known service for maps and location and it provides its SDKs for multiple platforms from the web to the mobile. Within the mobile, there are SDKs for Android, iOS, ReactNative and Flutter, but none for Xamarin/.NET iOS.</p>
<p>Recently, I successfully created and published <a target="_blank" href="https://www.nuget.org/packages/MapboxMapsObjC.iOS/"><strong>the NuGet package</strong></a> for integrating Mapbox iOS SDK to your Xamarin.iOS and/or .NET iOS app.</p>
<pre><code class="lang-plaintext">dotnet add package MapboxMapsObjC.iOS --version 11.4.0
</code></pre>
<p>In this blog, I will guide you through the steps to use that mentioned NuGet package in a .NET iOS app.</p>
<blockquote>
<p><strong><em>TLTR; You can access the full quickstart example from</em></strong> <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-mapbox-quickstart"><strong><em>this GitHub repo</em></strong></a><strong><em>.</em></strong></p>
</blockquote>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Visual Studio for Mac or Visual Studio on Windows</p>
</li>
<li><p>.NET 8.0.100</p>
</li>
<li><p>.NET for iOS workload</p>
</li>
</ul>
<h2 id="heading-steps"><strong>Steps</strong></h2>
<blockquote>
<p><strong><em>Assumption: You already have a .NET iOS app. If not, you can just create a new empty .NET iOS app to follow this guide.</em></strong></p>
</blockquote>
<h3 id="heading-1-config-your-mapboxdownloadstoken">1/ Config your <code>MAPBOX_DOWNLOADS_TOKEN</code></h3>
<p>Mapbox iOS SDK is distributed privately via Mapbox's CDN which we can configure SPM/CocoaPods to download or download directly the artifacts with a <code>MAPBOX_DOWNLOADS_TOKEN</code>. To know how to generate that token, please check out <a target="_blank" href="https://docs.mapbox.com/ios/maps/guides/install/">the official guide from Mapbox</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689652994872/36225840-dfc5-46ea-a9e4-9fc54ed8d652.png" alt class="image--center mx-auto" /></p>
<p>Xamarin.iOS/.NET iOS doesn't work with SMP/Cocoapods, but we can leverage <a target="_blank" href="https://www.nuget.org/packages/Xamarin.Build.Download">Xamarin.Build.Download package</a> to help us download the artifacts using Direct Download approach when building the app by following these steps:</p>
<ul>
<li><p>Generate/grab <code>MAPBOX_DOWNLOADS_TOKEN</code> from <a target="_blank" href="https://account.mapbox.com/"><strong>your Mapbox account</strong></a> page</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689553829929/aa7af92e-16aa-43ea-9ef2-549490c255cd.png?auto=compress,format&amp;format=webp" alt /></p>
</li>
<li><p>Amend your <code>.csproj</code> file with the following lines</p>
</li>
</ul>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">PropertyGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">MAPBOX_DOWNLOADS_TOKEN</span>&gt;</span>YOUR_MAPBOX_DOWNLOADS_TOKEN<span class="hljs-tag">&lt;/<span class="hljs-name">MAPBOX_DOWNLOADS_TOKEN</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">PropertyGroup</span>&gt;</span>
</code></pre>
<blockquote>
<p><strong><em>NOTE:</em></strong><code>MAPBOX_DOWNLOADS_TOKEN</code><strong><em>is a sensitive data item, it shouldn't be committed to our git repository. We should put it in an ignored file, but still understandable by the compiler.</em></strong></p>
</blockquote>
<h3 id="heading-2-addmapbox-ios-sdk-for-net-to-your-project"><strong>2/ Add</strong><code>Mapbox iOS SDK for .NET</code> to your project</h3>
<ul>
<li><p>Open NuGet Package Manager</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689653606105/999e8fbf-3e19-4901-a5f0-1d4d16b8686e.png" alt /></p>
</li>
<li><p>Search for the package <code>MapboxMapsObjC.iOS</code></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689653724804/32297bda-c226-4371-91c9-a4a6bf3b31f8.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Add it to your project</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689653782851/916dad92-67e2-45e8-874c-c50ef2681f95.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Try to build the project</p>
<blockquote>
<p><strong><em>A successful build means you did it well by far</em></strong></p>
</blockquote>
</li>
</ul>
<h3 id="heading-3-configure-mapboxaccesstoken">3/ Configure <code>mapbox_access_token</code></h3>
<p>To work with Mapbox APIs, we have to generate a personalized access token from <a target="_blank" href="https://account.mapbox.com/"><strong>our Mapbox account page</strong></a> and then put it into our project to use</p>
<ul>
<li><p>Generate/grab the access token</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689629809872/bee32c30-7025-4533-b653-eb5f227dbb59.png?auto=compress,format&amp;format=webp" alt /></p>
<blockquote>
<p><strong><em>You can use the default public token for this tutorial. If you want more control over what scopes to be used, please create a new token</em></strong></p>
</blockquote>
</li>
<li><p>Put the generated access token into <code>Info.plist</code></p>
<pre><code class="lang-plaintext">  &lt;key&gt;MBXAccessToken&lt;/key&gt;
  &lt;string&gt;YOUR_MAPBOX_ACCESS_TOKEN&lt;/string&gt;
</code></pre>
<blockquote>
<p><strong><em>NOTE:</em></strong><code>mapbox_access_token</code> is a sensitive data item, it shouldn't be committed to our git repository. We should put it in an ignored file, but still understandable by the compiler.</p>
</blockquote>
</li>
</ul>
<h3 id="heading-4-add-mapview-control-to-your-app">4/ Add <code>MapView</code> control to your app</h3>
<ul>
<li>Create <code>MapboxViewController</code></li>
</ul>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MapboxViewController</span> : <span class="hljs-title">UIViewController</span>
{
}
</code></pre>
<ul>
<li>Add <code>MapView</code> instance with default options in <code>ViewDidLoad</code></li>
</ul>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ViewDidLoad</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">base</span>.ViewDidLoad();

    <span class="hljs-comment">// Use all default options</span>
    <span class="hljs-keyword">var</span> options = MapInitOptionsFactory.CreateWithResourceOptions(
        <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>
        );

    <span class="hljs-keyword">var</span> mapView = MapViewFactory.CreateWithFrame(View.Bounds, options);
    mapView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;

    View.AddSubview(mapView);
}
</code></pre>
<ul>
<li>Change <code>RootViewController</code> in <code>AppDelegate.cs</code> file</li>
</ul>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">FinishedLaunching</span> (<span class="hljs-params">UIApplication application, NSDictionary launchOptions</span>)</span>
{
    <span class="hljs-comment">// create a new window instance based on the screen size</span>
    Window = <span class="hljs-keyword">new</span> UIWindow (UIScreen.MainScreen.Bounds);

    Window.RootViewController = <span class="hljs-keyword">new</span> MapboxViewController();

    <span class="hljs-comment">// make the window visible</span>
    Window.MakeKeyAndVisible ();

    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
</code></pre>
<ul>
<li><p>Check the result</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689656380924/ff769ee3-d64e-43d6-8a4f-445bfaaade9f.png" alt /></p>
</li>
</ul>
<h2 id="heading-wrap-up"><strong>Wrap up</strong></h2>
<p>I just guided you on how to use Mapbox in a .NET iOS project. You can do the same steps for a Xamarin.iOS project.</p>
<p>I want to port all native examples to .NET iOS, but it'll be a time-consuming task. Single me will last for a very long time. I hope the community can contribute to <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-mapbox-quickstart"><strong>this GitHub repo</strong></a> and/or to their repos.</p>
]]></content:encoded></item><item><title><![CDATA[How to use Mapbox in your .NET Android app]]></title><description><![CDATA[Overview
Mapbox is a well-known service for maps and location and it provides its SDKs for multiple platforms from the web to the mobile. Within the mobile, there are SDKs for Android, iOS, ReactNative and Flutter, but none for Xamarin/.NET Android.
...]]></description><link>https://tuyen-vuduc.tech/how-to-use-mapbox-for-your-dotnet-android-app</link><guid isPermaLink="true">https://tuyen-vuduc.tech/how-to-use-mapbox-for-your-dotnet-android-app</guid><category><![CDATA[mapbox]]></category><category><![CDATA[android app development]]></category><category><![CDATA[dotnet]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Mon, 17 Jul 2023 23:10:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1719535485329/d4882782-b522-4ad3-9487-3ee4c2f4fcb1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview">Overview</h2>
<p><a target="_blank" href="https://www.mapbox.com/">Mapbox</a> is a well-known service for maps and location and it provides its SDKs for multiple platforms from the web to the mobile. Within the mobile, there are SDKs for Android, iOS, ReactNative and Flutter, but none for Xamarin/.NET Android.</p>
<p>Recently, I successfully created and published <a target="_blank" href="https://www.nuget.org/packages/Com.Mapbox.Maps.Android/">the NuGet package</a> for integrating Mapbox Android SDKs to your Xamarin.Android and/or .NET Android app.</p>
<pre><code class="lang-bash">dotnet add package Com.Mapbox.Maps.Android --version 11.4.1
</code></pre>
<p>In this blog, I will guide you through the steps to use that mentioned NuGet package in a .NET Android app.</p>
<blockquote>
<p>TLTR; You can access the full quickstart example from <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-mapbox-quickstart">this GitHub repo</a>.</p>
</blockquote>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Visual Studio for Mac or Visual Studio on Windows</p>
</li>
<li><p>.NET 8.0.100</p>
</li>
<li><p>.NET for Android workload</p>
</li>
</ul>
<h2 id="heading-steps">Steps</h2>
<blockquote>
<p>Assumption: You already have a .NET Android app. If not, you can just create a new empty .NET Android app to follow this guide.</p>
</blockquote>
<h3 id="heading-1-config-your-mapboxdownloadstoken">1/ Config your <code>MAPBOX_DOWNLOADS_TOKEN</code></h3>
<p>Mapbox Android SDK is distributed privately via Mapbox's dedicated Maven repository which requires a personalized <code>MAPBOX_DOWNLOADS_TOKEN</code> to get the Gradle build to download the required artifacts.</p>
<blockquote>
<p>FYI: Mapbox Android SDK for .NET makes use of <a target="_blank" href="https://gradle.org">Gradle</a> to download its native dependencies.</p>
</blockquote>
<ul>
<li>Grab your MAPBOX_DOWNLOADS_TOKEN from <a target="_blank" href="https://account.mapbox.com/">your Mapbox account</a> page</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689553829929/aa7af92e-16aa-43ea-9ef2-549490c255cd.png" alt class="image--center mx-auto" /></p>
<ul>
<li>Put that token <code>~/.gradle/gradle.properties</code> from the terminal</li>
</ul>
<pre><code class="lang-xml">echo "MAPBOX_DOWNLOADS_TOKEN=YOUR_MAPBOX_DOWNLOADS_TOKEN" &gt;&gt; ~/.gradle/gradle.properties
</code></pre>
<blockquote>
<p>You can follow <a target="_blank" href="https://docs.mapbox.com/android/maps/guides/install/">the official guide</a> from Mapbox to complete this step.</p>
</blockquote>
<h3 id="heading-2-add-mapbox-android-sdk-for-net-to-your-project">2/ Add <code>Mapbox Android SDK for .NET</code> to your project</h3>
<ul>
<li><p>Open NuGet Package Manager</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689554827863/e05ab589-baa5-47bc-9f55-3b5d07190375.png" alt /></p>
</li>
<li><p>Search for the package <code>Com.Mapbox.Maps.Android</code></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689554864416/ef5f2577-33cb-43e0-9944-db4b866d0083.png" alt /></p>
</li>
<li><p>Add it to your project</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689554960449/61f232ce-f783-47eb-9b0a-fbca9785689c.png" alt /></p>
</li>
</ul>
<blockquote>
<p>NOTE: If you failed to add the nuget package, please check <code>Package Console</code> output window and resolve all mismatching dependencies. (I expect VS can do it for us, but it cannot)</p>
</blockquote>
<ul>
<li>Add these other required packages as well to resolve incompatibility issues</li>
</ul>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Xamarin.AndroidX.AppCompat"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"1.6.1.10"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Xamarin.AndroidX.Fragment "</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"1.7.0.2"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">PackageReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"Xamarin.AndroidX.Fragment.Ktx"</span> <span class="hljs-attr">Version</span>=<span class="hljs-string">"1.7.0.2"</span> /&gt;</span>
</code></pre>
<ul>
<li>Check the result</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719535598175/467f2b5e-80bf-4b78-9413-974380b4cfc6.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-3-add-mapbox-maven-repository-to-csproj">3/ Add Mapbox maven repository to <code>.csproj</code></h3>
<p>As I mentioned earlier, we will leverage Gradle, an Android build system, to resolve and download all Mapbox SDK's artifacts for us. To make that happen, we will need to amend our Android <code>.csproj</code> file.</p>
<ul>
<li><p>Open <code>.csproj</code> file in VS or VSCode</p>
</li>
<li><p>Add these lines</p>
</li>
<li><pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">GradleRepository</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"https://api.mapbox.com/downloads/v2/releases/maven"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Repository</span>&gt;</span>
        maven {
          url = uri("https://api.mapbox.com/downloads/v2/releases/maven")
          authentication {
            create<span class="hljs-symbol">&amp;lt;</span>BasicAuthentication<span class="hljs-symbol">&amp;gt;</span>("basic")
          }
          credentials {
            // Do not change the username below.
            // This should always be `mapbox` (not your username).
            username = "mapbox"
            // Use the secret token you stored in gradle.properties as the password
            password = providers.gradleProperty("MAPBOX_DOWNLOADS_TOKEN").get()
          }
        }
      <span class="hljs-tag">&lt;/<span class="hljs-name">Repository</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">GradleRepository</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>
</code></pre>
</li>
<li><p>Try to build the project</p>
<blockquote>
<p>A successful build means you did it well by far</p>
</blockquote>
</li>
</ul>
<h3 id="heading-4-configure-mapboxaccesstoken">4/ Configure <code>mapbox_access_token</code></h3>
<p>To work with Mapbox APIs, we have to generate a personalized access token from <a target="_blank" href="https://account.mapbox.com/">our Mapbox account page</a> and then put it into our project to use</p>
<ul>
<li><p>Generate/grab the access token</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689629809872/bee32c30-7025-4533-b653-eb5f227dbb59.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>You can use <em>the default public token</em> for this tutorial. If you want more control over what scopes to be used, please create a new token</p>
</blockquote>
</li>
<li><p>Copy the generated access token to <code>Resources/values/strings.xml</code></p>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">resources</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">string</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"mapbox_access_token"</span>&gt;</span>YOUR_MAPBOX_ACCESS_TOKEN<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">resources</span>&gt;</span>
</code></pre>
<blockquote>
<p>NOTE: <code>mapbox_access_token</code> is a sensitive data item, it shouldn't be committed to our git repository. We should put it in an ignored file, but still understandable by the compiler.</p>
</blockquote>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689630689187/6dd81d4e-d4b5-4b42-8bda-6e9bf500997b.png" alt /></p>
</li>
</ul>
<h3 id="heading-5-add-mapview-control-to-your-app">5/ Add <code>MapView</code> control to your app</h3>
<ul>
<li>In XML <code>Resources/layout/activity_main.xml</code></li>
</ul>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">com.mapbox.maps.MapView</span>
    <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/mapView"</span>
    <span class="hljs-attr">android:layout_width</span>=<span class="hljs-string">"match_parent"</span>
    <span class="hljs-attr">android:layout_height</span>=<span class="hljs-string">"match_parent"</span> /&gt;</span>
</code></pre>
<ul>
<li>Ensure your activity inherits from AppCompatActivity and use an AppCompat theme</li>
</ul>
<pre><code class="lang-csharp">[<span class="hljs-meta">Activity(
    Label = <span class="hljs-meta-string">"@string/app_name"</span>,
    MainLauncher = true,
    Theme = <span class="hljs-meta-string">"@style/Theme.AppCompat.Light.DarkActionBar"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MainActivity</span> : <span class="hljs-title">AppCompatActivity</span>
{
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<ul>
<li>Access <code>mapView</code> instance from C#</li>
</ul>
<pre><code class="lang-csharp">[<span class="hljs-meta">Activity(
    Label = <span class="hljs-meta-string">"@string/app_name"</span>,
    MainLauncher = true,
    Theme = <span class="hljs-meta-string">"@style/Theme.AppCompat.Light.DarkActionBar"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MainActivity</span> : <span class="hljs-title">AppCompatActivity</span>
{
    MapView mapView;
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnCreate</span>(<span class="hljs-params">Bundle? savedInstanceState</span>)</span>
    {
        <span class="hljs-keyword">base</span>.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.activity_main);
        mapView = FindViewById&lt;MapView&gt;(Resource.Id.mapView);
    }
}
</code></pre>
<ul>
<li>Override life-cycle methods</li>
</ul>
<pre><code class="lang-csharp">    <span class="hljs-comment">// ...</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnStart</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">base</span>.OnStart();
        mapView?.OnStart();
    }

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnStop</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">base</span>.OnStop();
        mapView?.OnStop();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnLowMemory</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">base</span>.OnLowMemory();
        mapView?.OnLowMemory();
    }

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OnDestroy</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">base</span>.OnDestroy();
        mapView?.OnDestroy();
    }
    <span class="hljs-comment">// ...</span>
</code></pre>
<ul>
<li>Check out the result</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689634126781/95c90f5b-c29d-40ba-aa00-d46b258851e5.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p><strong>NOTE</strong>: You can also initialize MapView instance directly from C# exactly in the same way you find in Mapbox native examples.</p>
</blockquote>
<h2 id="heading-wrap-up">Wrap up</h2>
<p>I just guided you on how to use Mapbox in a .NET Android project. You can do the same steps for a Xamarin.Android project.</p>
<p>I want to port all native examples to .NET Android, but it'll be a time-consuming task. Single me will last for a very long time. I hope the community can contribute to <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-mapbox-quickstart">this GitHub repo</a> and/or to their repos.</p>
]]></content:encoded></item><item><title><![CDATA[Mapbox for .NET MAUI now available]]></title><description><![CDATA[It's a long story and a huge effort to make this come true. It's the effort of more than 1 year of working, trying, stopping and resuming.

A/ The story and challenges
The story
I am the original author of Xamarin binding libraries for Mapbox SDKs as...]]></description><link>https://tuyen-vuduc.tech/mapbox-for-net-maui-now-available</link><guid isPermaLink="true">https://tuyen-vuduc.tech/mapbox-for-net-maui-now-available</guid><category><![CDATA[mapbox]]></category><category><![CDATA[#dotnet-maui]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[ios app development]]></category><category><![CDATA[android app development]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Sun, 16 Jul 2023 18:13:37 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689523980711/583d4b42-8bdb-4c3b-b721-01fc7124bdad.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>It's a long story and a huge effort to make this come true. It's the effort of more than 1 year of working, trying, stopping and resuming.</p>
</blockquote>
<h1 id="heading-a-the-story-and-challenges">A/ The story and challenges</h1>
<h2 id="heading-the-story">The story</h2>
<p>I am the original author of Xamarin binding libraries for Mapbox SDKs as well as the Xamarin.Forms component.</p>
<ul>
<li><p><a target="_blank" href="https://www.nuget.org/packages/Naxam.Mapbox.Droid/">Mapbox SDK for Xamarin.Android</a></p>
</li>
<li><p><a target="_blank" href="https://www.nuget.org/packages/Naxam.Mapbox.iOS/">Mapbox SDK for Xamarin.iOS</a></p>
</li>
<li><p><a target="_blank" href="https://www.nuget.org/packages/Naxam.Mapbox.Forms">Mapbox for Xamarin.Forms</a></p>
</li>
</ul>
<p>However, those libraries haven't got any updates since my company, <a target="_blank" href="https://naxam.net">Naxam</a>, stopped its business in 2020.</p>
<p>I had several requests to upgrade Mapbox SDKs binding libraries to its v10 and the Xamarin.Forms library as well in late 2020 or so because the previous versions will be deprecated and no longer receive support from Mapbox. I did try and also committed a deadline then failed to deliver because of many challenges.</p>
<h3 id="heading-the-challenges">The challenges</h3>
<h3 id="heading-a1-mapbox-sdk-for-android-is-no-longer-publicly-available-via-mvnrepositoryhttpsmvnrepositorycom">A.1/ Mapbox SDK for Android is no longer publicly available via <a target="_blank" href="https://mvnrepository.com/">MvnRepository</a></h3>
<p>We have to generate a MAPBOX_DOWNLOADS_TOKEN and config in a way that Gradle, a build system for Android, can read and download Mapbox artifacts from the private repository.</p>
<h3 id="heading-a2-mapbox-sdk-for-ios-is-no-longer-objective-c-compatible">A.2/ Mapbox SDK for iOS is no longer Objective-C compatible</h3>
<p>Xamarin.iOS can only work with Objective-C compatible APIs, however, Mapbox SDK for iOS v10.x makes use of a lot Swift-only APIs.</p>
<h3 id="heading-a3-xamarinforms-will-end-of-its-life-by-2024-and-will-be-replaced-by-net-maui">A.3/ Xamarin.Forms will end of its life by 2024 and will be replaced by .NET MAUI</h3>
<p>Xamarin.Forms is going to be deprecated and end its life by 2024, it's really a breaking change and prevent me from starting the upgrade immediately. The other thing, .NET MAUI, its successor, is totally different, I will have to rewrite the code entirely to get the best out of .NET MAUI.</p>
<h1 id="heading-bchallenges-are-cleared">B.Challenges are cleared</h1>
<h2 id="heading-b1-android-challenge">B.1/ Android challenge</h2>
<h3 id="heading-b11-the-overview">B.1.1/ The overview</h3>
<p>Creating a binding library for Xamarin.Android is "quite easy" if we have the native package at our hand then we simply embed it to our .NET DLL. However, it might violate the license from the native library provider, especially when the library is only available privately by downloading from its private maven repository. For a library with a public downloadable link, we can use X<a target="_blank" href="https://www.nuget.org/packages/Xamarin.Build.Download">amarin.Build.Download</a> library to download the library artifacts when creating a binding library as the practice from <a target="_blank" href="https://github.com/xamarin/GooglePlayServicesComponents/blob/d889e3e89e848470f1e56981ef3fd32acea259f2/source/GooglePlayServicesTargets.cshtml#L58">Google Play Services for Xamarin.Android repository</a>. This approach is great because of these advantages</p>
<ul>
<li><p>Avoid violating the license about the distribution within the given license e.g. it isn't allowed to redistribute ourselves</p>
</li>
<li><p>Make the .NET DLL/nuget package very lightweight</p>
</li>
</ul>
<p>However, it doesn't work if the native library is distributed privately via a private Maven repository.</p>
<h3 id="heading-b12-the-solution">B.1.2/ The solution</h3>
<p>I asked myself</p>
<blockquote>
<p>Is it possible to make use of Gradle, the native Android dependency management system? The final answer is YES.</p>
</blockquote>
<p>I tried many attempts and failed. I tried to use MavenNET library to download libraries from Maven repository, but it is over complicated and cannot make it work as expected. I tried again with the above idea and finally created <a target="_blank" href="https://www.nuget.org/packages/Dependencies.Gradle">Dependencies.Gradle</a> library which helps me create binding libraries for Xamarin.Android (.NET Android) where Android native libraries are managed by Gradle and we don't have to embed their artifacts within the .NET DLL/nuget package. It's an EUREKA for me.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">GradleImplementation</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"com.mapbox.maps:android:10.14.1"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">GradleImplementation</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">GradleRepository</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"https://api.mapbox.com/downloads/v2/releases/maven"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Repository</span>&gt;</span>
      maven {
        url 'https://api.mapbox.com/downloads/v2/releases/maven'
        authentication {
          basic(BasicAuthentication)
        }
        credentials {
          // Do not change the username below.
          // This should always be `mapbox` (not your username).
          username = "mapbox"
          // Use the secret token you stored in gradle.properties as the password
          password = MAPBOX_DOWNLOADS_TOKEN
        }
      }
      <span class="hljs-tag">&lt;/<span class="hljs-name">Repository</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">GradleRepository</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>
</code></pre>
<p>Please check out <a target="_blank" href="https://github.com/tuyen-vuduc/dotnet-binding-utils">dotnet-binding-utils repo for reference</a>. I will try to provide more documentation for the work so others can use it.</p>
<h2 id="heading-b2-ios-challenge">B.2 iOS challenge</h2>
<h3 id="heading-b21-the-overview">B.2.1/ The overview</h3>
<p>Xamarin.iOS is only able to work with Objective-C compatible APIs via P-Invoke technique. However, Swift is growing from year to year and has a lot of APIs that are no longer available to Objective-C. It's a real blocker for a Xamarin developer to create a binding library for a Swift-first iOS library. Unfortunately, Mapbox v10.x is a Swift-first iOS library. At first, I gave up because the Mapbox library has huge numbers of APIs and most of them are only available to be used within Swift.</p>
<p>Moreover, the other part, the library is distributed privately from Mapbox CDN. We have to provide a MAPBOX_DOWNLOADS_TOKEN to download.</p>
<h3 id="heading-b22-the-solution">B.2.2/ The solution</h3>
<p>The idea is very easy</p>
<p>1/ Make use of Xamarin.Build.Download</p>
<p>Luckily Mapbox allows to download iOS SDKs' artifacts via a link with the token in the URL, then Xamarin.Build.Download shines and helps us download the artifacts from the given link to the local machine.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span> <span class="hljs-attr">Condition</span>=<span class="hljs-string">"('$(OutputType)'!='Library' OR '$(IsAppExtension)'=='True')"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">XamarinBuildDownload</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"$(_MapboxMapsItemsFolder)"</span>&gt;</span>      
    <span class="hljs-tag">&lt;<span class="hljs-name">Url</span>&gt;</span>https://api.mapbox.com/downloads/v2/mobile-maps-ios/releases/ios/$(_MapboxVersion)/MapboxMaps.zip?access_token=$(_MapboxDownloadToken)<span class="hljs-tag">&lt;/<span class="hljs-name">Url</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Kind</span>&gt;</span>Zip<span class="hljs-tag">&lt;/<span class="hljs-name">Kind</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">XamarinBuildDownload</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>
</code></pre>
<p>Our job is only to associate the local artifacts with our projects.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span> <span class="hljs-attr">Condition</span>=<span class="hljs-string">"('$(OutputType)'!='Library' OR '$(IsAppExtension)'=='True')"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">NativeReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"$(_MapboxMapsSDKBaseFolder)artifacts\MapboxCoreMaps.xcframework"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Kind</span>&gt;</span>Framework<span class="hljs-tag">&lt;/<span class="hljs-name">Kind</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">SmartLink</span>&gt;</span>True<span class="hljs-tag">&lt;/<span class="hljs-name">SmartLink</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ForceLoad</span>&gt;</span>True<span class="hljs-tag">&lt;/<span class="hljs-name">ForceLoad</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">NativeReference</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>
</code></pre>
<p>2/ Create an Objective-C wrapper library</p>
<p>It's unavoidable, I had to handy code an Objective-C library to act as a bridge to work with the Mapbox iOS SDK. At first, I didn't know how to do it well. I used the builder pattern to wrap Swift only classes/struct and so on. It was very time-consuming because of the big amount of SDK available APIs. After months of living with the library, I finally figured out I can</p>
<ul>
<li><p>Make it simpler without using the builder pattern</p>
<ul>
<li><p>For some, I changed to use the factory pattern e.g. <code>MapInitOptionsFactory</code></p>
</li>
<li><p>I made use of extensions to the existing classes</p>
</li>
<li><p>I created wrapper classes where they simply hold Swift only instances and expose Objective-C compatible APIs</p>
</li>
<li><p>For two-way mapping of Swift only instances and Objective-C compatible instances, I have to create <code>mapTo</code> methods to help</p>
</li>
</ul>
</li>
<li><p>Write JS script to generate Objective-C compatible Swift classes/enums based on the original structures</p>
</li>
</ul>
<h2 id="heading-b3-net-maui-challenge">B.3 .NET MAUI challenge</h2>
<p>It's new and hasn't many documents about writing libraries. Moreover, working with VS for Mac is a real difficulty because of performance and responsiveness. Regardless of those, I finally made the library from scratch without or very little code from the Xamarin.Forms library.</p>
<p>It's a huge win because I can redesign the way the code is structured, remove unnecessary code and make it the .NET MAUI way.</p>
<p>To validate how it works, I ported about 10 examples of the iOS native examples to demonstrate. These examples are picky and for the most complex cases when using the SDKs.</p>
<p>To speed up the work, I also make use of code generators to generate stuff based on Swift code.</p>
<h1 id="heading-wrap-it-up">Wrap it up</h1>
<p>Finally, the binding libraries are now ready to use and so is the .NET MAUI library. Please check them out.</p>
<ul>
<li><p><a target="_blank" href="https://www.nuget.org/packages/Com.Mapbox.Maps.Android/">Mapbox for .NET Android</a></p>
</li>
<li><p><a target="_blank" href="https://www.nuget.org/packages/MapboxMapsObjC.iOS/">Mapbox for .NET iOS</a></p>
</li>
<li><p><a target="_blank" href="https://www.nuget.org/packages/Mapbox.Maui">Mapbox for .NET MAUI</a></p>
</li>
</ul>
<p><img src="https://pbs.twimg.com/media/FzlfrTyaQAAQY9d?format=jpg&amp;name=small" alt="Image" class="image--center mx-auto" /></p>
<p>We have an open issue is to build the app to the real iOS device. It's <a target="_blank" href="https://github.com/xamarin/xamarin-macios/issues/8917">a known issue</a> and I am trying to figure out how to resolve it.</p>
<p>The next step:</p>
<ul>
<li><p>Support v10.14.x</p>
</li>
<li><p>Fix iOS issue</p>
</li>
<li><p>Port more examples to .NET MAUI and as well as .NET Android/iOS</p>
</li>
<li><p>Write documentations</p>
</li>
</ul>
<p>It's a huge workload, I cannot make it happen without private sponsors. Thanks a lot to them. The repo can be found <a target="_blank" href="https://github.com/tuyen-vuduc/mapbox-maui">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[ChickAndPaddy - a .NET MAUI app template]]></title><description><![CDATA[Overview
.NET MAUI is the successor to Xamarin.Forms which ends the mission in 2024. .NET MAUI was expected to be GM in 2021 but was finally out of beta in the first half of 2022. .NET MAUI is a promise from Microsoft in the competence with Flutter, ...]]></description><link>https://tuyen-vuduc.tech/chickandpaddy-dotnet-maui-app-template</link><guid isPermaLink="true">https://tuyen-vuduc.tech/chickandpaddy-dotnet-maui-app-template</guid><category><![CDATA[#dotnet-maui]]></category><category><![CDATA[#maui]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[Mobile Development]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Sat, 14 Jan 2023 12:17:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673698599863/a453d67e-9c10-4d9d-8130-d2698d538c51.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview">Overview</h2>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/maui/what-is-maui?view=net-maui-7.0">.NET MAUI</a> is the successor to Xamarin.Forms which ends the mission in 2024. .NET MAUI was expected to be GM in 2021 but was finally out of beta in the first half of 2022. .NET MAUI is a promise from Microsoft in the competence with <a target="_blank" href="https://flutter.dev">Flutter</a>, a trending toolkit from Google, to overcome all the disadvantages of Xamarin.Forms.</p>
<p>Technically, .NET MAUI is rewritten from scratch to avoid all technical debts in Xamarin.Forms and leverage all the goodness from .NET6 and the latter. However, how the UI is rendered on each platform is almost the same as Xamarin.Forms as demonstrated below.</p>
<p><img src="https://learn.microsoft.com/en-us/dotnet/maui/media/what-is-maui/architecture.png?view=net-maui-7.0" alt=".NET MAUI architecture diagram." /></p>
<h2 id="heading-why-an-app-template">Why an app template?</h2>
<p>As mentioned above, .NET MAUI is quite <strong>new</strong>, so there aren't so many apps built out there for reference yet, neither publically nor personally. Building an app using Xamarin.Forms for years, I believe all the best practices I applied for Xamarin.Forms will benefit MAUI apps and I created <a target="_blank" href="https://github.com/tuyen-vuduc/chick-and-paddy-dotnet-maui">Chick and Paddy MAUI app template</a>.</p>
<h2 id="heading-what-the-template-provides">What the template provides</h2>
<h3 id="heading-1-a-clean-feature-based-architecture">1) A clean, feature-based architecture</h3>
<p>I am a fan of clean and feature-based (module-based) architecture, I tried to apply it where I can regardless of the technologies I used. Here is the screenshot of the high-level architecture of the template.</p>
<p><img src="https://raw.githubusercontent.com/tuyen-vuduc/chick-and-paddy-dotnet-maui/main/docs/images/arch.png" alt class="image--center mx-auto" /></p>
<p>In this template, the external dependencies are all just a few</p>
<ul>
<li><p>Community Toolkit - MVVM</p>
</li>
<li><p>Community Toolkit - MAUI</p>
</li>
</ul>
<p>It'll be thin and light to make the best out of the default. We might not need other fancy libraries and/or frameworks to help.</p>
<h3 id="heading-2-common-mvvm-classes">2) Common MVVM classes</h3>
<p>Base classes will reassure the consistency of the overall architecture. In this template, such classes inherit from MVVM Community Toolkit base classes (ObservableObject, ObservableValidator) to leverage all the common best practices and code generators from Microsoft.</p>
<ul>
<li><p><strong>BaseModel</strong> -- The base class for data model classes.</p>
</li>
<li><p><strong>BaseFormModel</strong> -- The base class for form model classes that require validations on their properties.</p>
</li>
<li><p><strong>BaseViewModel</strong> -- The base class for all ViewModel classes.</p>
</li>
<li><p><strong>NavigationAwareBaseViewModel</strong> - The base class for ViewModel classes which needs to handle navigation events and data.</p>
</li>
</ul>
<h3 id="heading-3-hiding-maui-from-viewmodel-with-appnavigator">3) Hiding MAUI from ViewModel with AppNavigator</h3>
<p>Starting from Xamarin.Forms, <strong>Shell</strong> was introduced to simplify the navigation with .NET mobile apps. However, it'll be a <strong>bad practice</strong>, in MHO, if we use that class directly in our ViewModel to do navigation. I introduced <strong>IAppNavigator</strong> as the contract for all navigation operations and <strong>AppNavigator</strong> to hide <strong>Shell</strong> methods from <strong>ViewModel</strong>s.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> Task <span class="hljs-title">NavigateAsync</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> target, <span class="hljs-keyword">bool</span> animated = <span class="hljs-literal">false</span>, <span class="hljs-keyword">object</span> args = <span class="hljs-keyword">default</span></span>)</span>
    {
        <span class="hljs-comment">// ... preconfigure</span>
        <span class="hljs-keyword">return</span> MainThread.InvokeOnMainThreadAsync(() =&gt; Shell.Current.GoToAsync(
            target,
            animated,
            navArgs
        );
    }
</code></pre>
<p>With <strong>IAppNavigator</strong>, we can do UnitTesting of our <strong>ViewModel</strong>s without the knowledge of <strong>Shell</strong> at all.</p>
<p><img src="https://raw.githubusercontent.com/tuyen-vuduc/todo-app-xamarin-forms/master/docs/images/page-lifecycles.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-4-descriptive-readme-andamp-code-generation-script">4) Descriptive readme &amp; code generation script</h3>
<p>I wrote a very descriptive readme to help the other developers to get started with the template in a few minutes.</p>
<p>The readme is written with a lot of code snippets, outlined details and diagrams to understand better the design idea.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673697668845/56a4c06d-0e9c-4fa0-9988-4aa4a03c112e.png" alt class="image--center mx-auto" /></p>
<p>The code generation script will simplify the way we create a new page that strictly follows my pattern.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673697720975/63d96c7a-5ace-45b3-b4de-ff946141d8c0.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-5-easter-eggs">5) Easter eggs</h3>
<ul>
<li><p><strong>EdgeInsetExtension</strong> -- a markup extension to simplify the way we handle Thickness properties like Padding, Margin - it was inspired by Flutter way.</p>
</li>
<li><p>Many others: reusable converters, markup extensions, and naming conventions.</p>
</li>
</ul>
<h2 id="heading-final-thoughts">Final thoughts</h2>
<p><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/maui/what-is-maui?view=net-maui-7.0">.NET MAUI</a> still requires a lot of effort to get to the speed of its competitors, but it's ready to use for your new mobile apps if you want to use .NET or migrate your Xamarin.Forms apps. I already built apps for my clients using MAUI and <a target="_blank" href="https://github.com/tuyen-vuduc/chick-and-paddy-dotnet-maui">this template</a>.</p>
<p>I hope this template will benefit you guys as well.</p>
<p>Happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[Open source BusuuClone app using Xamarin.Native and MvvmCross]]></title><description><![CDATA[Overview
In 2017, I found up Naxam with the aim to build a great software development company specialized in Microsoft technologies, started with Xamarin. Following my way of learning, I chosen a pet project for the first Nxers to learn and practice ...]]></description><link>https://tuyen-vuduc.tech/open-source-busuuclone-app-using-xamarinnative-and-mvvmcross</link><guid isPermaLink="true">https://tuyen-vuduc.tech/open-source-busuuclone-app-using-xamarinnative-and-mvvmcross</guid><category><![CDATA[mvvmcross]]></category><category><![CDATA[xamarin-ios]]></category><category><![CDATA[xamarin-android]]></category><category><![CDATA[Xamarin]]></category><category><![CDATA[MVVM]]></category><dc:creator><![CDATA[Tuyến Vũ]]></dc:creator><pubDate>Sat, 26 Nov 2022 17:30:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1669569663151/Ec72lmarr.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-overview">Overview</h1>
<p>In 2017, I found up Naxam with the aim to build a great software development company specialized in Microsoft technologies, started with Xamarin. Following my way of learning, I chosen a pet project for the first Nxers to learn and practice their Xamarin skills. I chose <a target="_blank" href="https://www.busuu.com/">Busuu</a>, a world leading application for learning English skills, because it has a beautiful UI/UX and I strongly believe my Nxers can improve both technical skills and English skills through out the project.</p>
<blockquote>
<p>Nxers ~ Naxam's people</p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/vHXcBHpL3Ow">https://youtu.be/vHXcBHpL3Ow</a></div>
<h1 id="heading-what-we-gained">What we gained</h1>
<ul>
<li>A bulk of features cloned are runnable with mock data</li>
<li>All people joined can do Xamarin project at various levels of confidence</li>
<li>All people understood how to form up a good architecture for a Xamarin.Native app</li>
<li>The company had its first Xamarin capable team to handle incoming projects</li>
</ul>
<h1 id="heading-why-i-made-it-open-sourced">Why I made it open sourced</h1>
<p>This pet project was made private and only available for internal Nxers to refer to while building a Xamarin.Native application. However, Naxam is no longer in business for years now, I think this pet project will benefit other developers around the world when finding a good MvvmCross sample application specifically and a .NET application in general.</p>
<p>This project is also to show the way I normally organize a .NET application where I am the tech lead of the project.</p>
<p>Please find out <a target="_blank" href="https://github.com/NAXAM/busuu-clone-xamarin-app">the repo on Github</a>. Please star if it benefits you.</p>
<h1 id="heading-credits">Credits</h1>
<p>I would like to say "Thank you" to all of my Nxers who had been working with me for that so long. This project was made by them and always belongs to them. You can find their github in <a target="_blank" href="https://github.com/NAXAM/busuu-clone-xamarin-app/blob/main/README.md">the README of the project</a>.</p>
<blockquote>
<p>NOTE: This project was made in the early of 2017, so the referenced libraries are outdated by now.</p>
</blockquote>
]]></content:encoded></item></channel></rss>