When developing cross-platform mobile applications with .NET MAUI (or previously Xamarin), you may encounter situations where your app works perfectly with public APIs but fails when connecting to internal network services. These issues often stem from HTTP client implementation differences, certificate validation, and TLS compatibility. This article explores how to identify, troubleshoot, and resolve these common networking challenges.
Understanding HTTP Client Options in MAUI/Xamarin
In the MAUI/.NET ecosystem, developers have access to two primary HTTP client implementations:
Consistent behavior across different operating systems
May handle SSL/TLS differently than platform-native implementations
Uses the .NET certificate validation system
2. Native HttpClient (Android’s implementation)
Leverages the platform’s native networking stack
Typically offers better performance on the specific platform
Uses the device’s system certificate trust store
Follows platform-specific security policies and restrictions
Switching Between Native and Managed HttpClient
In MAUI Applications
MAUI provides a flexible handler registration system that lets you explicitly choose which implementation to use:
// In your MauiProgram.cs
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureMauiHandlers(handlers =>
{
// Use the managed implementation (Microsoft's .NET HttpClient)
handlers.AddHandler(typeof(HttpClient), typeof(ManagedHttpMessageHandler));
// OR use the native implementation (platform-specific)
// handlers.AddHandler(typeof(HttpClient), typeof(PlatformHttpMessageHandler));
});
return builder.Build();
}
In Xamarin.Forms Legacy Applications
For Xamarin.Forms applications, set this in your platform-specific initialization code:
// In MainActivity.cs (Android) or AppDelegate.cs (iOS)
HttpClientHandler.UseNativePlatformHandler = false; // Use managed handler
// OR
HttpClientHandler.UseNativePlatformHandler = true; // Use native handler
Creating Specific Client Instances
You can also explicitly create HttpClient instances with specific handlers when needed:
// Use the managed handler
var managedHandler = new HttpClientHandler();
var managedClient = new HttpClient(managedHandler);
// Use the native handler (with DependencyService in Xamarin)
var nativeHandler = DependencyService.Get<INativeHttpClientHandler>();
var nativeClient = new HttpClient(nativeHandler);
Using HttpClientFactory (Recommended for MAUI)
For better control, testability, and lifecycle management, consider using HttpClientFactory:
// In your MauiProgram.cs
builder.Services.AddHttpClient("ManagedClient", client => {
client.BaseAddress = new Uri("https://your.api.url/");
})
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler());
// Then inject and use it in your services
public class MyApiService
{
private readonly HttpClient _client;
public MyApiService(IHttpClientFactory clientFactory)
{
_client = clientFactory.CreateClient("ManagedClient");
}
}
Common Issues and Troubleshooting
1. Self-Signed Certificates
Internal APIs often use self-signed certificates that aren’t trusted by default. Here’s how to handle them:
// Option 1: Create a custom handler that bypasses certificate validation
// (ONLY for development/testing environments)
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};
var client = new HttpClient(handler);
For production environments, instead of bypassing validation:
Add your self-signed certificate to the Android trust store
Configure your app to trust specific certificates
Generate proper certificates from a trusted Certificate Authority
2. TLS Version Mismatches
Different Android versions support different TLS versions by default:
Android 4.1-4.4: TLS 1.0 by default
Android 5.0+: TLS 1.0, 1.1, 1.2
Android 10+: TLS 1.3 support
If your server requires a specific TLS version:
// Force specific TLS versions
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
3. Network Configuration
Ensure your app has the proper permissions in the AndroidManifest.xml:
Test with both HTTP client implementationsSwitch between native and managed implementations to isolate whether the issue is specific to one implementation
Test the API endpoint outside your appUse tools like Postman or curl on the same network
Enable logging for network calls
// Add this before making requests
HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "YourApp/1.0");
Capture and inspect network trafficUse Charles Proxy or Fiddler to inspect the actual requests/responses
Check certificate information
# On your development machine
openssl s_client -connect your.internal.server:443 -showcerts
Verify which implementation you’re using
var client = new HttpClient();
var handlerType = client.GetType().GetField("_handler",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic)?.GetValue(client);
Console.WriteLine($"Using handler: {handlerType?.GetType().FullName}");
Debug specific errors
For Java.IO.IOException: “Trust anchor for certification path not found” – this means your app doesn’t trust the certificate
For HttpRequestException with “The SSL connection could not be established” – likely a TLS version mismatch
Conclusion
When your MAUI Android app connects successfully to public APIs but fails with internal network services, the issue often lies with HTTP client implementation differences, certificate validation, or TLS compatibility. By systematically switching between native and managed HTTP clients and applying the troubleshooting techniques outlined above, you can identify and resolve these networking challenges.
Remember that each implementation has its advantages – the native implementation typically offers better performance and follows platform-specific security policies, while the managed implementation provides more consistent cross-platform behavior. Choose the one that best fits your specific requirements and security considerations.
I have been using XPO from DevExpress since day one. For me is the best O.R.M in the dot net world, so when I got the news that XPO was going to be free of charge I was really happy because that means I can use it in every project without adding cost for my customers.
Nowadays all my customer needs some type of mobile development, so I have decided to master the combination of XPO and Xamarin
Now there is a problem when using XPO and Xamarin and that is the network topology, database connections are no designed for WAN networks.
Let’s take MS SQL server as an example, here are the supported communication protocols
TCP/IP.
Named Pipes
To quote what Microsoft web site said about using the protocols above in a WAN network
In a fast-local area network (LAN) environment, Transmission Control Protocol/Internet Protocol (TCP/IP) Sockets and Named Pipes clients are comparable with regard to performance. However, the performance difference between the TCP/IP Sockets and Named Pipes clients becomes apparent with slower networks, such as across wide area networks (WANs) or dial-up networks. This is because of the different ways the interprocess communication (IPC) mechanisms communicate between peers.”
So, what other options do we have? Well if you are using the full DotNet framework you can use WCF.
So, it looks like WCF is the solution here since is mature and robust communication framework but there is a problem, the implementation of WCF for mono touch (Xamarin iOS) and mono droid (Xamarin Android)
You can read about Xamarin limitations in the following links
I don’t want to go into details about how the limitation of each platform affects XPO and WCF but basically the main limitation is the ability to use reflection and emit new code which is needed to generate the WCF client, also in WCF there are problems in the serialization behaviors.
So basically, what we need to do is to replace the WCF layer with some other technology to communicate to the database server
The technology I’ve selected for this AspNetCore which I would say is a really nice technology that is modern, multi-platform and easy to use. Here below you can see what is the architecture of the solution
AspNetCore
Rest API
So, what we need basically is to be able to communicate the data layer with the data store through a network architecture.
The network architecture that I have chosen is a rest API which is one of the strong fronts of AspNetCore. The rest API will work as the server that forward the communication from XPO to the Database and vice versa, you can find a project template of the server implementation here https://www.jocheojeda.com/download/560/ this implementation references one nuget where I have written the communication code, you can fine the nuget here https://nuget.bitframeworks.com/feeds/main/BIT.Xpo.AgnosticDataStore.Server/19.1.5.1
You know that moment when you are about to deliver your next mobile app, everything is working fine in your development environment but once you release the app to your customers you start getting errors like the one below
You think, what happened? everything was running fine on my development environment. Well, lets said that when you compile your Xamarin application the main goal of the compiler and the linker is reduced the size of the app, so a lot of things get stripped out of the final release.
Some of the things that are stripped out of the final release are the code pages and that can cause crashes in your app not because your code depends on them but because of some nugets or third-party libraries do.
So to avoid having the exception “System.NotSupportedException: Encoding 1252 data could not be found. Make sure you have correct international codeset assembly installed and enabled” you just need to explicitly add code page to your application
For iOS projects, include it by checking west under Project Properties -> iOS Build -> Internationalization:
For Android projects, include it by checking west under Project Properties -> Android Build -> Linker -> Internationalization:
For visual studio for windows here are the screenshots
iOS
Android
The credit goes to this post, that saved my life fixing this error StackOverflow
For a long time now, I have wanted to access the file system when I create a Xamarin Forms UAP/UWP application but that was actually impossible … till now. After the Windows 10 build 17134 update its possible to access the broad file system, the approach is not straight forward.
To gain access to the file system in your Xamarin Forms UAP/UWP application follow these steps
1) Go the properties of your UAP/UWP application and check the targeting, the minimum should be at least 16299, what I recommend is 171344
You can also change the targets unloading the project and editing the csproj file
2) In your solution explorer edit your Package.appxmanifest by selecting it and press F7, looking the file from the top should look like the image below
Add this namespace xmlns:rescap=”http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities and update the IgnorableNamesSpaces like this IgnorableNamespaces=”uap mp rescap” after the changes your file should look like the image below
3) Lookup for the capabilities node in the manifest and add a new capability <rescap:Capability Name=”broadFileSystemAccess” /> your capabilities section should look like the image below
4) Rebuild your application, then select it on the solution explorer, right click over it and click on deploy, this will register the application in your OS
5) on your Windows OS go to settings>File system privacy settings and you will see all the UAP/UWP applications that are registered in your OS and have access to the file system, here you can allow/deny the access to the file system in general or by application
6) now everything is ready for your app to access the file system, but there is a little catch, in most cases, you cannot use the classes in system.io to access the file system you have to use Windows.Storage.Storagefolder below is a code snippet that illustrates how to use such class
public async void GetDirectories(string sDir)
{
var folder = await StorageFolder.GetFolderFromPathAsync(sDir);
foreach (var file in await folder.GetFilesAsync())
{
Debug.WriteLine(file.Name);
}
}
I have created a sample app using these steps, you can download the source from GitHub
From time to time I realize that a lot of the stuff I’m writing is repetitive and complex, so that is the perfect time to create a new code rush custom template, behold the new template xfbp (xamarin forms bindable property). Download the template here [download id=”211″]
After 15 years, DevExpress has finally made XPO available free-of-charge. If you’re not familiar with XPO, you can learn more about its feature set here. If you’ve used XPO in the past or are familiar with capabilities, you will love this.
As we already know a Xamarin ListView is populated with data using the ItemsSource property, which can accept any collection implementing IEnumerable but if we want the ListView to automatically update as items are added, removed or changed in the underlying list, you’ll need to use an ObservableCollection. Here is where XpoObservableCollection becomes the best friend for all the XPO fans out there.
XpoObservableCollection inherit from an XPCollection so to use, it is exactly as you would use an XPCollection, the only difference is that the XpoObservableCollection refresh the state of ListViews on Xamarin Forms.
XamarinXpoPageSelector takes it a step further by internally implementing XPPageSelector and presenting the XpoObservableCollection as a pageable collection. With this in mind, on the constructor of the XamarinXpoPageSelector you need to pass the following parameters:
XpoObservablePageSelectorBehavior = AppendPage or SinglePage.
Use Append in case you want to add the results of the new page to the collection or Single page to clear the last page results before showing the new page.
And that’s it. The same awesome ObservableRangeCollection (from MVVM Helpers) that adds important methods such as AddRange, RemoveRange, Replace, and ReplaceRange, it is now available in XPO and of course, it is open source so go and take a look behind the curtains.