Previously I wrote about playing audio with .NET MAUI on Android, iOS, Mac Catalyst, and Windows. The main problem with my implementation was that the FilePicker
for picking local audio files only worked on Windows. On iOS/Android it let you browse the device, and displayed audio files, but it didn’t let you select an audio file. On Mac Catalyst it did nothing at all. I was convinced these were MAUI bugs. Turns out I was completely wrong!
The problem was this block of code:
var pickedAudio = await FilePicker.Default.PickAsync(new PickOptions
{
PickerTitle = "Select audio file",
FileTypes = new FilePickerFileType(
new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.WinUI, new [] { "*.mp3", "*.m4a" } },
{ DevicePlatform.Android, new [] { "*.mp3", ".3gp", ".mp4", ".m4a", ".aac", ".ts", ".amr", ".flac", ".mid", ".xmf", ".mxmf", ".rtttl", ".rtx", ".ota", ".imy", ".mkv", ".ogg", ".wav" } },
{ DevicePlatform.iOS, new[] { "*.mp3", "*.aac", "*.aifc", "*.au", "*.aiff", "*.mp2", "*.3gp", "*.ac3" } }
})
});
The problem was ultimately caused by the order in which I wrote and tested the code on each platform. I did Windows first, followed by Android and iOS. Also, note the lack of an entry for Mac Catalyst.
So, I started off by adding code for Windows and specified *.mp3
and *.m4a
as the file extensions to display in the FilePicker
. This was purely because I couldn’t find any docs on which audio file formats are supported in WinUI 3. I got the MauiAudioPlayer
working on Windows using these file extensions and moved onto Android.
I assumed that I could just use the same file extensions on Android and iOS. I managed to find docs from Google and Apple that mentioned the audio file formats they supported, and what their file extensions are, hence the more extensive entries for each platform. The issue was that I didn’t know that file picking on Android and iOS, using FilePicker
and FilePickerFileType
, doesn’t require an array of file extensions.
It was Gerald who alerted me to this, by pointing to the Xamarin.Essentials PickOptions.FileTypes
API docs. Sure, it’s for Xamarin.Essentials rather than .NET MAUI but the same content applies. Specifically:
On Android and iOS the files not matching this list is only displayed
grayed out. When the array is null or empty, all file types can be
selected while picking. The contents of this array is platform
specific; every platform has its own way to specify the file types. On
Android you can specify one or more MIME types, e.g. “image/png”; also
wild card characters can be used, e.g. “image/*”. On iOS you can
specify UTType constants, e.g. UTType.Image. On UWP, specify a list of
extensions, like this: “.jpg”, “.png”.
So there’s the answer: on Android you specify the file types as MIME types, and wild cards can be used. On iOS you specify UTType
constants, and on Windows you use file extensions.
I looked at common MIME types and discovered that for audio the MIME types are entries like audio/aac
, audio/mpeg
etc. When combined with wildcards it meant that the MIME type for all audio files supported by a platform is audio/*
. So this became the string
entry in the FilePickerFileType
dictionary for Android.
iOS was a thornier problem. UTType
constants are the recommended approach and are documented here. The problem I have with this approach is it requires iOS14+. I wanted my code to work with all versions of iOS that MAUI supports (which the docs says is iOS10+). On a random StackOverflow post I read that you could use system-defined uniform type identifiers to specify which files can be selected, which lead me to Apple’s System-declared Uniform Type Identifiers doc. A quick search of the doc revealed that the correct constant for audio files is public.audio
. So this became the string
entry in the FilePickerFileType
dictionary for iOS, that I also duplicated for Mac Catalyst.
This lead to the following code:
var pickedAudio = await FilePicker.Default.PickAsync(new PickOptions
{
PickerTitle = "Select audio file",
FileTypes = new FilePickerFileType(
new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.WinUI, new [] { "*.mp3", "*.m4a" } },
{ DevicePlatform.Android, new [] { "audio/*" } },
{ DevicePlatform.iOS, new[] { "public.audio" } },
{ DevicePlatform.MacCatalyst, new[] { "public.audio" } }
})
});
When combined with a couple of small updates to MauiAudioPlayer
on iOS/Android/Mac Catalyst, to correctly load the selected file, all platforms gained the ability to play locally stored audio files. The PR of changes to enable this can be found here.
So there we have - a .NET MAUI audio player for iOS, Android, Mac Catalyst, and Windows, that plays audio from URLs, audio embedded in your app package, and local audio files on your device that can be chosen by the user. You can find the code here.