Category Archives: Uncategorized

Configure Mobile.BuildTools in .NET MAUI to keep your secrets safe

I had some issues at first with compiling after doing my first migration from Xamarin to .NET MAUI with Mobile.BuildTools but I went back to some of mother other apps and found my old solution to a new problem.

The nuget works great in MAUI and I am more comfortable with it than some of the newer methods to keep secrets out of your source. So here is a quick overview of what it takes to configure it and get it working in your .NET MAUI or Xamarin app.

Understanding the configuration file for the nuget is key. There are some subtle things in the config that need to be set properly in order for it to work right. Here is an example config for your to look at.

{
"$schema": "https://mobilebuildtools.com/schemas/v2/buildtools.schema.json",
"appSettings": {
"YourProjectName": [
{
"accessibility": "Internal",
"className": "TheActualClassToUseToReferenceHere",
"delimiter": ";",
"namespace": "ThisIsTheNameSpaceButAlsoNeedsToExistAsAFolderInYourProjectInamedMineHelpers",
"rootNamespace": "AppsRootNamesSpaceIEMyCoolApp",
"properties": [ //*PUT YOUR SECRETS IN THIS SECTION WITH NAME/TYPE PAIRING LIKE BELOW. PS REMOVE THIS TEXT OR YOU WILL HAVE ERRORS*//
{
"name": "ApiKey",
"type": "String"
},
{
"name": "FunctionUri",
"type": "String"
},
{
"name": "EndPoint",
"type": "String"
}
]
}
]
},
"appConfig": {
"strategy": "TransformOnly"
},
"artifactCopy": {
"disable": false
},
"automaticVersioning": {
"behavior": "Off",
"environment": "All",
"versionOffset": 0
},
"css": {
"minify": false,
"bundleScss": false
},
"images": {
"directories": [],
"conditionalDirectories": {
"Debug": [],
"!Debug": [],
"iOS": [],
"Android": []
}
},
"manifests": {
"token": "$",
"variablePrefix": "Manifest_",
"missingTokensAsErrors": false,
"disable": false
},
"releaseNotes": {
"maxDays": 7,
"maxCommit": 10,
"characterLimit": 250,
"filename": "ReleaseNotes.txt",
"createInRoot": false,
"disable": false
},
"environment": {
"defaults": {},
"configuration": {
"Debug": {}
}
},
"debug": true
}
view raw buildtools.json hosted with ❤ by GitHub

So in the JSON file the basics I commented on are to set the project name, class name (I made a folder called Helpers and put “Helpers” there in that spot), and root namespace of the app. Then you can enter in your secret NAME/TYPE pairs, so it can be a string or whatever. Generally, it will be strings for your secrets but what you name them here is a reference in the actual secrets file itself.

The secrets file should be named after the class name you defined in your JSON file. So if you called it AppSecrets then you would name your secrets file “appsecrets.json.” Pretty straightforward forward but it can lead to problems later on that are hard to figure out without some debugging. In your secrets file you use the NAMES defined in your config as KEY/VALUE pairs in it and it is formatted in ordinary JSON format like below.

{
"ApiKey": "YOURAPIKEYHERE",
"EndPoint": "https://someendpoint.com",
"FunctionUri": "https://afunction.url"
}

Now in code, you refer to your secrets by the Class Name then a dot then the Secret Name you input into your config. So you would refer to ApiKey as “AppSecrets.ApiKey.” So in your code, you can refer to it like this, “var myapikey = AppSecrets.ApiKey.”

The rest can be ignored if you are not using the full capabilities of Mobile.BuildTools.

If you run into a compile error complaining about build tools that refer to some weird message about build properties, here is what you need.

<Project>
<Target Name="MBTHack"
BeforeTargets="Secrets"
DependsOnTargets="MobileBuildToolsInit">
</Target>
</Project>

Name it like in the gist and then place that file with your .sln file in the root of your project so MS Build can find it. Then the error should go away, I don’t remember the exact specifics of it but the file remedies that.

Adding A Foreground Service To Your .NET MAUI Android App – Updated Feb 14, 2023

It’s not well documented and I had to scrape stuff together from several sources and post on a couple of different forums but I have a working foreground service that puts up a notification in the notification tray that is tappable to reopen the app after you close it. Here is the code to enable it by file in your project:

using Android.App;
using Android.Content;
using Android.OS;
using AndroidX.Core.App;
namespace YourMAUIApp;
[Service]
public partial class MyForeGroundService : Service, IService
{
public const string NOTIFICATION_CHANNEL_ID = "10276";
private const int NOTIFICATION_ID = 10923;
private const string NOTIFICATION_CHANNEL_NAME = "notification";
private void StartForegroundService()
{
var intent = new Intent(Android.App.Application.Context, typeof(MainActivity));
var pendingIntentFlags = Build.VERSION.SdkInt >= BuildVersionCodes.S
? PendingIntentFlags.UpdateCurrent |
PendingIntentFlags.Mutable
: PendingIntentFlags.UpdateCurrent;
var pendingIntent = PendingIntent.GetActivity(Android.App.Application.Context, 0, intent, pendingIntentFlags);
var notifcationManager = GetSystemService(NotificationService) as NotificationManager;
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
CreateNotificationChannel(notifcationManager);
}
var notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
notification.SetContentIntent(pendingIntent);
notification.SetAutoCancel(false);
notification.SetOngoing(true);
notification.SetSmallIcon(Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu ? Resource.Drawable.ic_notification : Resource.Mipmap.appicon);
notification.SetContentTitle("My Aoo");
notification.SetContentText("My App Service is running");
StartForeground(NOTIFICATION_ID, notification.Build());
}
private static void CreateNotificationChannel(NotificationManager notificationMnaManager)
{
var channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME,
NotificationImportance.Low);
notificationMnaManager.CreateNotificationChannel(channel);
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
StartForegroundService();
return StartCommandResult.Sticky;
}
public void Start()
{
var intent = new Intent(Android.App.Application.Context, typeof(MyForeGroundService));
Android.App.Application.Context.StartForegroundService(intent);
}
public void Stop()
{
var intent = new Intent(Android.App.Application.Context, typeof(MyForeGroundService));
Android.App.Application.Context.StopService(intent);
}
}
namespace YourMAUIApp;
public partial class HomePage : ContentPage
{
public readonly HomeViewModel ViewModel;
public IService Service { get; }
public HomePage(IService _service)
{
InitializeComponent();
ViewModel = new HomeViewModel();
Service = _service;
}
private void Button_OnClicked(object sender, EventArgs e)
{
Service.Start();
}
private void StopButton_OnClicked(object sender, EventArgs e)
{
Service.Stop();
}
}
namespace YourMAUIApp;
public interface IService
{
void Start();
void Stop();
}
view raw IService.cs hosted with ❤ by GitHub
namespace YourMAUIApp;
public static class MauiProgram
{
public static IServiceProvider Services { get; private set; }
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.Services.AddTransient<IService, MyForeGroundService>();
builder.Services.AddTransient<HomePage>();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.UseMauiCommunityToolkitCore()
.UseMauiCommunityToolkitMarkup()
.ConfigureFonts(fonts =>
{
fonts.AddFont("Roboto-Regular.ttf", "RobotoRegular");
})
var app = builder.Build();
Services = app.Services;
return app;
}
}
view raw MauiProgram.cs hosted with ❤ by GitHub

They are out of order for some reason but first, you want to create the interface in the root of your project called IService.cs with two methods Start and Stop. Just copy the example and adjust the namespace. After that is done, in your android folder create the foreground class, name it what you want but use the inherited interface and Service class. Once that is done, edit MauiProgram.cs to add the entries I have in the example for the service, the property, and the two builder services, and then end as I have it where it calls those services and then calls app. On your home page make a property referencing the service and then also a reference to them in the constructor as well to initialize the service.

At this point, you can either call it through a button or have it launch automatically however you want it from that page or whichever page you choose.

Now I am assuming you are only targeting Android in your project, if you are not then your need to create dummy classes in your other project folders that reference the IService interface so that there are no issues and then in whichever page you choose to launch the service from use:

#if ANDROID #ENDIF around the Service.Start()/Service.Stop() to isolate to just Android or if you were smart and blackhole the interface references in your other classes’ implementations then don’t worry about it 🙂

John

Edit!

You need one more thing if you’re targeting Android 13 and up. That would be the Post Notification permission and getting that from the user. Currently .NET MAUI essentials doesn’t have that built in but if you dig into the docs on Microsoft you’ll find out how to create an extension class to add it. Also, in your AndroidManifest.xml you need to add the following:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Then just do like normal before starting the service, check for the notification permission and request it if it’s not granted.

Edit 2!

If you’re building a Shell App and you see a crash when bringing the app off the back stack then here is the work around!

In your MainActivity.cs add Launchmode = Launchmode.SingleTop where I have it in the example below and it should go away.

using Android.App;
using Android.Content.PM;
namespace YourApp.Platforms.Android;
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density, LaunchMode = LaunchMode.SingleTop)]
public class MainActivity : MauiAppCompatActivity
{
}
view raw MainActivity.cs hosted with ❤ by GitHub

Edit 3!

Found this this morning during debugging! In your AndroidManifest.xml in the Application section add the following entry.

android:enableOnBackInvokedCallback=”true

John

[wpedon id=3183]

Apples New UIDevice.Name Entitlement

If you were like me and trying to figure out how to implement the new entitlement to get the iOS and iPadOS Device Name Entitlement to work then look no further!

First after you get your entitlement approval, go to the developers portal and go to your apps Identification entry and a new tab for entitlements will have showed up. There you will find the UIDevice.Name entitlement. Check it and save it. Then go to your profile for development and deployment and just save them again to ensure the new entitlement are attached with the ID is secured to the profiles. I’m not sure if you need a new development and deployment certs but I would revoke the old ones and just make a new set to be sure.

Once that is done, go into the key chain on your Mac and delete the old certs and install the new certs. Export P12’s and import them into your Visual Studio IDE (I would clean out the old certs first under %APPDATA%/Local/Xamarin/iOS) so that is has them as well on the Windows side and reimport the profiles from the developer portal. I would reimport the profiles into XCode and also VS Mac as well.

Once that is done, on your Mac/Windows machine edit your Entitlements.Plist and add the following line to it:

<key>com.apple.developer.device-information.user-assigned-device-name</key> <true/>

Do a clean and you should be able to do a build and see the device name now if all went well. I went through so many iterations of trying things and not finding information on the internet for doing it through MAUI that I hope that I have guided you in the right direction!

John

Is .NET MAUI ready now?

The short answer is maybe and the long answer is no. I started my next project totally Xamarin but when the news hit that although they were not sunsetting it until the end of 2023, the last big update to Xamarin.iOS was the last.

So it quickly turned into a .NET MAUI project and the conversion process is not a “day long” event like Microsoft put in their blog. It was more like a month and spending 1700 bucks on new MAUI controls. So, no Microsoft it’s not a day to convert.

Speaking of controls Telerik needs to lower their price because what you’re getting is in beta at best.

App Center support is alpha at best and not all features implemented. And don’t even think of targeting .NET 7 either as they only started working on supporting 6.

So my summary is they pushed .NET MAUI as fast as they could leaving all the support entities behind. I mean really it’s like the control and other providers are playing catch-up and .NET MAUI just plows on leaving what makes an app complete in the dust.

John

After thought for me is AppCenter is a big mess and barely supporting. NET 6. They are never going to catch up with .NET MAUI. Crashes is weird and Analytics is throwing an exception whenever called. I’ve submitted bug reports on them, hopefully they’ll get fixed.

Looking ahead!

Azure SSM is nearly complete, just lacking queue support. I plan on doing that in December as there doesn’t seem to be much to it. Also, I’ll be converting it to MAUI since they have really killed Xamarin for good.

In the mean time I have been busy raising funds and working on my next big project. Is gonna be really nice I think.

John

File Shares Are Done

The SDK is not the best to code against but they’re in the app now and it’s two thirds complete with the finish line coming in December with the final storage component to add.

I’ll be releasing it this week once I get directions and media done for the Share support. So be on the lookout for an update soon.

It will be important with File Shares to read the documentation as nested directories cause certain issues to arise and very specific procedures for dealing with moving and deleting them.

John

New Update to Azure SSM

I’m rolling out the new update to Azure Simple Storage Manager on Thursday after work. I’ll be using Translate My Lingo to handle the updates to the stores and I am thinking about hosting a Zoom session to go over how it works and how it really can be free to use (once you buy it of course LOL) to do your store translations.

If there is enough interest I’ll do that, if you are interested email me at [email protected]. Once I see how many are interested I’ll send out the Zoom meeting details.

It should take about 45 minutes to an hour because the Apple Store update is so different from the Google store.

John