<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ruud Kobes, Author at XABLU</title>
	<atom:link href="https://www.xablu.com/author/rkobes/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.xablu.com/author/rkobes/</link>
	<description>We build Ventures, Products and Teams</description>
	<lastBuildDate>Wed, 19 Nov 2025 12:07:46 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>

<image>
	<url>https://www.xablu.com/wp-content/uploads/2019/08/xablu-favicon-100x100.png</url>
	<title>Ruud Kobes, Author at XABLU</title>
	<link>https://www.xablu.com/author/rkobes/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>.NET MAUI: Implementing platform behavior</title>
		<link>https://www.xablu.com/2021/09/17/net-maui-implementing-platform-behavior/</link>
		
		<dc:creator><![CDATA[Ruud Kobes]]></dc:creator>
		<pubDate>Fri, 17 Sep 2021 15:16:07 +0000</pubDate>
				<category><![CDATA[Mobile Strategies]]></category>
		<category><![CDATA[Technical]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[Maui]]></category>
		<category><![CDATA[net6]]></category>
		<category><![CDATA[Planet Xamarin]]></category>
		<guid isPermaLink="false">https://www.xablu.com/?p=15134</guid>

					<description><![CDATA[<p>.NET MAUI is right around the corner. Like its predecessor it offers sharing most code while building for multiple platforms. This includes the UI part. Of course there are still [&#8230;]</p>
<p>The post <a href="https://www.xablu.com/2021/09/17/net-maui-implementing-platform-behavior/">.NET MAUI: Implementing platform behavior</a> appeared first on <a href="https://www.xablu.com">XABLU</a>.</p>
]]></description>
										<content:encoded><![CDATA[		<div data-elementor-type="wp-post" data-elementor-id="15134" class="elementor elementor-15134" data-elementor-settings="{&quot;ha_cmc_init_switcher&quot;:&quot;no&quot;}" data-elementor-post-type="post">
						<section class="elementor-section elementor-top-section elementor-element elementor-element-b984a0c elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="b984a0c" data-element_type="section" data-e-type="section" data-settings="{&quot;_ha_eqh_enable&quot;:false}">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-75b630c" data-id="75b630c" data-element_type="column" data-e-type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-510f200 elementor-widget elementor-widget-text-editor" data-id="510f200" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
									<p>.NET MAUI is right around the corner. Like its predecessor it offers sharing most code while building for multiple platforms. This includes the UI part. Of course there are still elements that require access to the platform, like accessing the camera, or something less common like customizing HttpMessageHandler behavior on iOS.</p><p>For common use-cases Xamarin.Essentials was offered to Xamarin Forms users. Now with MAUI this library will be included in your application by default (named MAUI Essentials), making it easy to use SecureStorage or implement permissions on Android and iOS. Additionally there is a <a class="ql-link" href="https://github.com/CommunityToolkit/Maui" target="_blank" rel="noopener noreferrer">MAUI Community Toolkit</a><span data-void-detect="true"> which, as the name implies, provides free tools: effects, converters, controls and more.</span></p>								</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-7bbef8b elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="7bbef8b" data-element_type="section" data-e-type="section" data-settings="{&quot;_ha_eqh_enable&quot;:false}">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-ce9f99f" data-id="ce9f99f" data-element_type="column" data-e-type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-ee5e8e8 elementor-widget elementor-widget-heading" data-id="ee5e8e8" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
					<h2 class="elementor-heading-title elementor-size-default">Manual platform implementation</h2>				</div>
				</div>
				<div class="elementor-element elementor-element-f286e94 elementor-widget elementor-widget-text-editor" data-id="f286e94" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
									<p>In some apps .NET MAUI with the Essentials pack and the Xamarin Community Toolkit can be sufficient. For most apps though, there is a point where some custom adjustment is necessary. A good example would be control adjustment using Handlers or Effects. We&#8217;ll describe Handlers and their value in more detail in a future blogpost.</p><p data-block-id="block-5c88f9cb-d68c-494a-bbc2-222d047d9dbf">There are several ways to go about defining platform specific behavior in MAUI. Because of multi-targeting the code will be in the single shared project. With Xamarin Forms this was also possible using the Shared Project architecture, but the recommended and most common approach was using a PCL and implementing platform behavior using dependency injection. Let&#8217;s run through some options!</p>								</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-ee9454c elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="ee9454c" data-element_type="section" data-e-type="section" data-settings="{&quot;_ha_eqh_enable&quot;:false}">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-bd68bd1" data-id="bd68bd1" data-element_type="column" data-e-type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-44a0481 elementor-widget elementor-widget-heading" data-id="44a0481" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
					<h3 class="elementor-heading-title elementor-size-default">Using #if</h3>				</div>
				</div>
				<div class="elementor-element elementor-element-66213b0 elementor-widget elementor-widget-text-editor" data-id="66213b0" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
									<div>To add a few lines of platform-specific code to an otherwise shared class we can use compiler directives like <code>#if __IOS__.</code> This is a quick and easy solution and keeps the related code close together, in the same file. For smaller handler mappings this could be a nice and easy to manage approach. It could also make Effects easier, because we don&#8217;t need the routed effects and DI registration of every platform implementation. With larger sections of platform-specific code this can get messy and harder to read though.</div>								</div>
				</div>
				<div class="elementor-element elementor-element-bbe6743 elementor-widget elementor-widget-code-highlight" data-id="bbe6743" data-element_type="widget" data-e-type="widget" data-widget_type="code-highlight.default">
				<div class="elementor-widget-container">
							<div class="prismjs-default copy-to-clipboard ">
			<pre data-line="14,16,18" class="highlight-height language-csharp line-numbers">
				<code readonly="true" class="language-csharp">
					<xmp>//SecureSettingsProvider.cs

namespace MyApp.Settings;

public class SecureSettingsProvider
{
    public string GetSecureSetting(string context, string key)
    {
        return GetLocalSetting($"{context}-{key}");
    }

    private string GetLocalSetting(string contextSpecificKey)
    {
        #if __IOS__
        // retrieve value from secure storage / keychain
        #elif __ANDROID__
        // do the same for android
        #endif
    }
}</xmp>
				</code>
			</pre>
		</div>
						</div>
				</div>
				<div class="elementor-element elementor-element-2071603 elementor-widget elementor-widget-spacer" data-id="2071603" data-element_type="widget" data-e-type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
							<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-a6039d3 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="a6039d3" data-element_type="section" data-e-type="section" data-settings="{&quot;_ha_eqh_enable&quot;:false}">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-e90512f" data-id="e90512f" data-element_type="column" data-e-type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-d6103d8 elementor-widget elementor-widget-heading" data-id="d6103d8" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
					<h3 class="elementor-heading-title elementor-size-default">Using platform folders</h3>				</div>
				</div>
				<div class="elementor-element elementor-element-664f9b6 elementor-widget elementor-widget-text-editor" data-id="664f9b6" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
									<p>MAUI projects using the single project structure will provide platform folders out-of-the-box. They will be compiled conditionally by default, e.g. the Android folder will only be included when building for Android. Within this option there are still 2 distinctive approaches:</p>								</div>
				</div>
				<div class="elementor-element elementor-element-cc62deb elementor-widget elementor-widget-heading" data-id="cc62deb" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
					<h4 class="elementor-heading-title elementor-size-default">Dependency injection using interfaces</h4>				</div>
				</div>
				<div class="elementor-element elementor-element-93ccf2c elementor-widget elementor-widget-text-editor" data-id="93ccf2c" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
									<div>Like the old-school Xamarin Forms pattern, using an interface in the cross-platform code, and explicit implementations in each platform folder. Any logic that could be shared will have to be copied between the implementations, although to make it slightly more complex, one could also define a cross-platform base class.</div>								</div>
				</div>
				<div class="elementor-element elementor-element-1d9a91d elementor-widget elementor-widget-heading" data-id="1d9a91d" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
					<h4 class="elementor-heading-title elementor-size-default">Partial classes</h4>				</div>
				</div>
				<div class="elementor-element elementor-element-26802da elementor-widget elementor-widget-text-editor" data-id="26802da" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
									<div>Partial classes are not a new concept in C#. Check <a class="ql-link" href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods" target="_blank" rel="noopener noreferrer">the docs</a><span data-void-detect="true"> if you want to read more about them. We can define one cross-plaform partial class and another partial class with the same name and namespace in each platform folder. This is a pretty clean alternative to using #if directives when working with larger classes/code sections. A potential disadvantage of this approach is that both partial classes live quite far-away from each other, which could hinder discoverability and increase cognitive load, if there is a lot of shared code. If the partial class has little shared code, the experience is a lot like using interfaces.</span></div>								</div>
				</div>
				<div class="elementor-element elementor-element-2bbf542 elementor-widget elementor-widget-code-highlight" data-id="2bbf542" data-element_type="widget" data-e-type="widget" data-widget_type="code-highlight.default">
				<div class="elementor-widget-container">
							<div class="prismjs-default copy-to-clipboard ">
			<pre data-line="" class="highlight-height language-csharp line-numbers">
				<code readonly="true" class="language-csharp">
					<xmp>//SecureSettingsProvider.cs in shared code

namespace MyApp.Settings;
public partial class SecureSettingsProvider.cs
{
    public string GetSecureSetting(string context, string key)
    {
        var settingValue = GetLocalSetting($"{context}-{key}");

        // do some work that's shared between implementations
        if(String.IsNullOrEmtpy(something))
          return "nothing";
        else
          return settingValue;
    }
}</xmp>
				</code>
			</pre>
		</div>
						</div>
				</div>
				<div class="elementor-element elementor-element-0102824 elementor-widget elementor-widget-code-highlight" data-id="0102824" data-element_type="widget" data-e-type="widget" data-widget_type="code-highlight.default">
				<div class="elementor-widget-container">
							<div class="prismjs-default copy-to-clipboard ">
			<pre data-line="" class="highlight-height language-javascript line-numbers">
				<code readonly="true" class="language-javascript">
					<xmp>//SecureSettingsProvider.cs in Android platform folder

namespace MyApp.Settings;
public partial class SecureSettingsProvider.cs
{
    private string GetLocalSetting(string contextSpecificKey)
    {
        // retrieve secure setting value for Android
    }
}</xmp>
				</code>
			</pre>
		</div>
						</div>
				</div>
				<div class="elementor-element elementor-element-fae696c elementor-widget elementor-widget-spacer" data-id="fae696c" data-element_type="widget" data-e-type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
							<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-4f17b6e elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="4f17b6e" data-element_type="section" data-e-type="section" data-settings="{&quot;_ha_eqh_enable&quot;:false}">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-de75dbc" data-id="de75dbc" data-element_type="column" data-e-type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-1b9e025 elementor-widget elementor-widget-heading" data-id="1b9e025" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
					<h3 class="elementor-heading-title elementor-size-default">Using filename based conditional compiling</h3>				</div>
				</div>
				<div class="elementor-element elementor-element-b4adb09 elementor-widget elementor-widget-text-editor" data-id="b4adb09" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
									<p data-block-id="block-4440b66d-9328-442d-99a7-dac9ac7a5432">This pattern is used in the MAUI github repository itself. Concretely this again means using partial classes, but this time the platform specific implementation is defined in the same folder in a separate file. For example for our <code>SecureS</code><code>ettingsProvider</code>, we would have a partial class in <code>SecureSettingsProvider.cs</code> and another one in <code>SecureSettingsProvider.Android.cs</code>, <img decoding="async" class="size-full wp-image-15137 alignright" src="https://www.xablu.com/wp-content/uploads/2021/09/functionality-grouping-multitargeting.png" alt="" width="292" height="107" srcset="https://www.xablu.com/wp-content/uploads/2021/09/functionality-grouping-multitargeting.png 292w, https://www.xablu.com/wp-content/uploads/2021/09/functionality-grouping-multitargeting-100x37.png 100w" sizes="(max-width: 292px) 100vw, 292px" /><code>SecureSettingsProvider.iOS.cs</code> , etc.<br />A potential advantage of this is that platform implementations are directly next to the shared part, which improves discoverability and can reduce cognitive load.</p><div data-block-id="block-47ce594c-5a02-48f4-aeed-56e1ada477c1">This approach is not supported out of the box though. The compiler needs to be informed which files to include or more accurately, which NOT to include, so we need to add this information to our project file. For an example of how this could look, see the first +-30 lines in <a href="https://github.com/dotnet/maui/blob/main/.nuspec/Microsoft.Maui.Controls.MultiTargeting.targets" target="_blank" rel="noopener noreferrer">MultiTargeting.targets</a><span data-void-detect="true"> file in the MAUI repository. The following sample is taken from line 2-7 there and slightly adjusted:</span></div>								</div>
				</div>
				<div class="elementor-element elementor-element-f12ba5d elementor-widget elementor-widget-code-highlight" data-id="f12ba5d" data-element_type="widget" data-e-type="widget" data-widget_type="code-highlight.default">
				<div class="elementor-widget-container">
							<div class="prismjs-default copy-to-clipboard ">
			<pre data-line="" class="highlight-height language-xml line-numbers">
				<code readonly="true" class="language-xml">
					<xmp><ItemGroup Condition="$(TargetFramework.StartsWith('Xamarin.iOS')) != true AND $(TargetFramework.StartsWith('net6.0-ios')) != true AND $(TargetFramework.StartsWith('net6.0-maccatalyst')) != true ">
  <Compile Remove="**\**\*.iOS.cs" />
  <None Include="**\**\*.iOS.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup></xmp>
				</code>
			</pre>
		</div>
						</div>
				</div>
				<div class="elementor-element elementor-element-de29544 elementor-widget elementor-widget-text-editor" data-id="de29544" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
									<div data-block-id="block-47ce594c-5a02-48f4-aeed-56e1ada477c1">This ItemGroup definition informs the compiler to ignore all <code>*.iOS.cs</code> files in the project unless the current build target is Xamarin.iOS, net6.0-ios or .net6.0-maccatalyst. Similar project configuration can also be used to allow for custom platform folders thoughout the code:</div>								</div>
				</div>
				<div class="elementor-element elementor-element-697da10 elementor-widget elementor-widget-code-highlight" data-id="697da10" data-element_type="widget" data-e-type="widget" data-widget_type="code-highlight.default">
				<div class="elementor-widget-container">
							<div class="prismjs-default copy-to-clipboard ">
			<pre data-line="" class="highlight-height language-xml line-numbers">
				<code readonly="true" class="language-xml">
					<xmp>  <Compile Remove="**\iOS\**\*.cs" />
  <None Include="**\iOS\**\*.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" /></xmp>
				</code>
			</pre>
		</div>
						</div>
				</div>
				<div class="elementor-element elementor-element-f08aa8c elementor-widget elementor-widget-spacer" data-id="f08aa8c" data-element_type="widget" data-e-type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
							<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-92e45a8 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="92e45a8" data-element_type="section" data-e-type="section" data-settings="{&quot;_ha_eqh_enable&quot;:false}">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-f50c498" data-id="f50c498" data-element_type="column" data-e-type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-a8fc026 elementor-widget elementor-widget-heading" data-id="a8fc026" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
					<h2 class="elementor-heading-title elementor-size-default">Which option is best?</h2>				</div>
				</div>
				<div class="elementor-element elementor-element-3496340 elementor-widget elementor-widget-text-editor" data-id="3496340" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
									<p>As always in software development, the answer here is &#8220;it depends&#8221;. For multitargeting libraries, like MAUI itself, it is likely a good option to look at the the filename or nested folder approach we discussed. With a little project setup this allows you to group code by functionality, keeping related code close together.</p><p data-block-id="block-4a8872a6-8e32-423d-8e43-360a54819071">For apps, it depends on how much platform-specific code really needs to be written. For some apps only a few adjustments may be needed using Effects, Behaviors and handler mappings. It is very possible for these to be only a few lines which allows for keeping everying in one file using #if directives. If these grow it is convenient to use the existing platform folders. In both these alternatives you can forego the need to use interfaces and register the platform services for dependency injection which can be convenient.</p><p data-block-id="block-4a8872a6-8e32-423d-8e43-360a54819071">Please note that we&#8217;ve been looking at these patterns fairly strictly from MAUI perspective. Of course it is possible that you prefer to use interfaces to making testing and mocking easier. In that case you will be setting up the DI container anyway. It may still be easier to register one implementation and use above options, instead of registering every platform service separately, but keep in mind your overall architecture before choosing any of these patterns.</p>								</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				</div>
		<p>The post <a href="https://www.xablu.com/2021/09/17/net-maui-implementing-platform-behavior/">.NET MAUI: Implementing platform behavior</a> appeared first on <a href="https://www.xablu.com">XABLU</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
