I first added NFC code to 32feet.NET back in 2020. However, as other priorities came along it never got fully polished and released on NuGet. This year I finally got around to it and updated the code for .NET 8.0 as it had originally targeted Xamarin Forms.
InTheHand.Nfc is available on NuGet and follows the API model of WebNFC – it supports reading and writing NDEF tags as this is the most widely used format for providing Urls, text and other custom data types. Although the original Windows codebase is still there it is largely untouched and untested because there is no definitive NFC hardware for Windows and the external Sony reader I had been using is no longer supported. I was able to install the drivers and get it running but I’m not currently able to read tags through the API. Since iOS and Android are more important platforms for this functionality I’ve not spent any more time on the Windows implementation yet.
There is a sample .NET MAUI app to demonstrate how to use the library. However it should work on any other .NET based platform (Uno, Avalonia etc). There are some platform specific setup items to observe – NFC reading requires permission on both Android and iOS. For iOS you must also add the NFC Reading capability to your app id.
The code is quite simple. It’s all async and uses Events for detecting tags. You can either pass a cancellation token to control when to stop scanning or if you don’t it will automatically stop after the first tag is scanned.
Hopefully you’ll find this useful for adding NFC reading/writing to your .NET apps. I do want to re-enable the Windows functionality, and make sure everything described in the sample is also documented in the Wiki. The only missing functionality from WebNFC is the ability to make a tag read-only; if there is a demand I could look at adding this too.
Deep-linking is the ability to define a Url which will direct the user straight to a specific feature of your app. On Android these are called App-links, for iOS they tend to be called Universal links. These can be an app-specific uri scheme or you can, with the right configuration on your web server, link a web domain so that you can redirect web links to the equivalent page in your app. I’m going to be working with the first type here.
.NET MAUI allows you to support deep-linking on both platforms once you’ve set up the necessary handler code in each native project. This passes the received Uri to the Application class so you can handle parsing the Uri and navigating in a single place. I’m not going to go over this process because it is described in the MAUI documented for Android and iOS.
This allows your app to be launched from a browser, QR Code, or another app. However the default experience on Android for an NFC tag results in a “New tag collected” dialog which asks you if you want to follow the link. Luckily, there is a way around this. You have to specify an additional action in your IntentFilter. Alongside Intent.ActionView you can add NfcAdapter.ActionNdefDiscovered. With this added your app is correctly registered to receive intents from Uri tags and will launch immediately when presented with your specific app link.
The great thing about implementing this is you may already be using deep-linking and you can support links from NFC tags without actually having to implement any NFC reading code within your app. You can have a consistent mechanism for working with links from a variety of sources.
I’ve been asked a number of times about the process to ask for Bluetooth permission for your app. I’ve been meaning to create something reusable for some time but it has languished on the To Do list for rather too long. I recently noticed that as of .NET 8.0, MAUI will incorporate a Bluetooth permission which simplifies the process for asking for permission in your app in a clean cross-platform way.
I used this as an opportunity to refactor the library that I had started so I could create the same pattern for Xamarin Forms and .NET MAUI 7.0 so you can get started with it now and then switch to the in-box equivalent when you update your code. You can request permission with a single line of code:
PermissionStatus status = await Permissions.RequestAsync<InTheHand.Bluetooth.Permissions.Bluetooth>();
It provides a simple way to request permission, which on some platforms won’t do anything noticeable but on others, such as Android, it will correctly prompt for permission as long as you’ve set up your manifest correctly.
There is a particular issue when writing any code which will run on Android which presents external UI through an Intent or uses broadcasts in that you need to have a reference to the current Activity. I covered this in my last post.
Following on from that I moved the code to a new library (since it is required in both the Bluetooth Classic and Bluetooth LE libraries). It now provides a simple API which you can use to determine the current Activity without adding any framework dependencies.
InTheHand.AndroidActivity.CurrentActivity
The code can determine this on Xamarin Forms, .NET MAUI and Uno Platform. I haven’t figured out if there is a clean way to do this on Avalonia yet but there is a simple workaround for everything currently unsupported – including if you want to use the libraries from “native” Xamarin Android or .NET 6.0 Android and beyond.
Every Android project contains a main Activity. For an Avalonia mobile app this is in MainActivity.cs and is an empty class derived from AvaloniaMainActivity. Simply override the OnCreate method like so:-
protected override void OnCreate(Bundle savedInstanceState)
{
// provide reference to current activity
InTheHand.AndroidActivity.CurrentActivity = this;
base.OnCreate(savedInstanceState);
}
Natively, Android apps are built around activities. These are distinct parts of an application which include UI and are designed to perform a particular task. Android allows you to start activities and pass data between them. A number of system dialogs are just activities which run and return a result.
Across Platforms
When using a cross-platform framework, whether it be Xamarin Forms, Uno or .NET Maui your app will generally have only a single Activity. You can create a complex application and never touch the template generated activity code. However when you want to call into an Activity, whether it be your own, a third-party library or a system feature, you need to have a reference to your current activity in order to launch another.
Each framework has its own method to help with this. On Xamarin Forms this is exposed in the Xamarin.Essentials library – Xamarin.Essentials.Platform.CurrentActivity. When migrating to .NET Maui this moved to the Microsoft.Maui.Essentials library – Microsoft.Maui.ApplicationModel.Platform.CurrentActivity. The new project templates add a line of code to the default MainActivity to initialize this and store a reference to the running activity.
On Uno the Uno base library includes Uno.UI.ContextHelper.Current which stores a reference to the activity and is initialised in the base Microsoft.UI.Xaml.ApplicationActivity class.
What’s the point of all this?
If we want to create a .NET library which works on all these different frameworks and has access to the activity we have some work to do. We can’t create a NuGet targeting different frameworks because these are all net6.0-android – you can’t specify the UI tech. We don’t want to provide multiple virtually-identical libraries tweaked for each framework, but we also don’t want to reference multiple dependencies in one library. The solution is a bit of good old-fashioned reflection. By checking for the presence of each of these types we can query the property and have our activity reference to use as required. Of course, none of these may be present. It’s entirely property our library is used from a “native” .NET Android app and the developer is working directly with the Android APIs. We can still provide a valuable API here but provide a method to pass the activity context to any API which requires it to display some UI. That allows us to cover all bases from a single dll for all “flavours” of .NET Android development. From your cross-platform code it’s entirely transparent. I’ve put my current code into a Gist here:-
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The good news from this is that InTheHand.BluetoothLE (as of the next NuGet drop) no longer has Xamarin or Maui dependencies but works just the same on these platforms. I’m interested in extending this to support Avalonia too. I may also move the code into its own library as I envision using it from a few places and it will be easier to maintain.
While debugging an app I came across this new message in the Output window:-
"Not wrapping exception of type Java.IO.IOException from method `Read`. This will change in a future release."
When working with an RFComm service on Android you use a BluetoothSocket which in turn owns two Streams – one for Input and one for Output. Currently when there is a failure such as a broken connection the Read or Write operation will throw a Java.IO.IOException even though these streams are exposed as regular .NET Streams. It looks like Microsoft plan to change this behaviour and I presume this will be to wrap the exception in the equivalent System.IO.IOException. This makes a lot of sense and will improve consistency with other .NET flavours but it is something you need to be aware of as it will require code changes in your exception handling.