Category Archives: software development

First steps with MAUI: some friction

I am evaluating Microsoft’s MAUI (Multi-platform app UI) for use on a cross-platform project. I mainly develop on a Mac with Visual Studio Code.

Setup for MAUI development is well explained here though getting everything in place for Mac Catalyst, iOS and Android is tedious. There is a tricky issue related to Apple’s Xcode, needed for MAUI for the Apple targets. Apple’s recommendation is that you install Xcode from the App Store. As you would expect, it updates quite frequently and always when there is a new version of macOS. The version on my machine is therefore 26.6. After installing MAUI for .NET 10 I got an error when trying to build, stating that I had the wrong version (mine was too new).

The workaround for this is either to install multiple versions of Xcode, which is fiddly, or to set ValidateXcodeVersion to false in the .csproj project file.

As it turns out, there is another fix. The .NET for iOS SDK (used by MAUI) was updated last month to require Xcode 26.6. Note: require, not support. Each version of the SDK requires one and only one version of Xcode, and there is a lag between Apple releasing a new version, and Microsoft updating its SDKs.

My instinct is to set ValidateXcodeVersion false during development and to watch out for strange errors that might be fixed for a production build, using the correct Xcode version.

Microsoft’s MAUI SDK engineering lead said:

Updates can include different things that amount to varying levels of work. Sometimes there’s api changes in the SDKs Xcode ships that we need to account for, sometimes they change Cli tools or break things in other ways. Depending on the changes it can be a little bit of work or a lot. 

Lately minor version updates to Xcode tend to be somewhat compatible with existing MAUI (Mac/ios) workloads and you can technically force trying to use it with the right build settings, but we generally advise not installing Xcode from the App Store so that it isn’t silently updated and you can install updates when you know they’ll be supported by MAUI instead.

This is friction, though it comes with the cross-platform territory. Note that Apple insists on building with the latest Xcode for store submission.

With that out of the say, I started building an experimental app. This is not Windows and conceptually one should think of the app UI as a single page, mobile app style, even if developing for Windows and Mac Catalyst. I decided to use a Tab Bar for navigation, but ran into an issue: when debugging the app for Mac Catalyst, the Tab Bar did not appear.

I thought this was something I was doing wrong, even though it was a simple and basic case. Then after digging a bit I discovered that it was a bug, that I was not the first to hit it, and that it was fixed with a workaround late last year.

The solution is to run: dotnet workload update from the command line to get the latest version.

Both issues are a reminder of how tricksy cross-platform development can be, particularly for frameworks like MAUI which use native widgets rather then drawing them from scratch like Flutter or Avalonia UI.

The solution, it seems to me, is to keep the user interface simple.

Vercel’s Scriptc is a TypeScript to native code compiler – how did we get here?

Vercel has previewed Scriptc on GitHub, a native compiler for TypeScript which is open source under the Apache 2 license.

Scriptc takes TypeScript code as input and outputs a native code executable, along with an embedded JavaScript engine if needed, using the existing QuickJS ng project. The JavaScript engine is needed if there is code included that cannot be statically compiled, including JavaScript code, such as npm dependencies, or code typed as “any” in TypeScript.

Scriptc runs on Apple Silicon and uses cross-compilation to target Linux and Windows. The project claims some big improvements in performance: startup time for an example workload reduced from 47ms to 2.4ms, memory usage down from around 100MB to 4MB, binary size reduced from around 80MB to 3MB.

How Scriptc compiles TypeScript to native code

The Node.js API is supported as well as the TypeScript language and the idea seems to be that server applications running on Node could be compiled to native using this tool.

Although this sounds promising, comments on Hacker News are not entirely favourable. “The architecture is idiotic, they have zero credible perf numbers,” said one, claiming that the “project reeks of weapons grade AI psychosis.” Expanding on this, complaints include the use of QuickJS, using floats for numbers, and resorting too quickly to an embedded JavaScript engine.

Vercel as a company is enthusiastic about AI and Scriptc appears to have been made largely with AI.

When Microsoft introduced a native code TypeScript to JavaScript compiler in TypeScript 7, greatly reducing compilation time, the awkward question was: if native code is so much faster than JavaScript, why not use a more performant language for the application code as well?

How did we get here? It all started with Brendan Eich’s LiveScript for Netscape Navigator, renamed JavaScript when first released in December 1995, and the beginning of interactive web pages. Part of the thinking behind JavaScript was that it could be used to script Java applets – Java components embedded in web pages – but JavaScript itself was always more popular. Adobe used a variant of JavaScript, called ActionScript, for its Flash add-in which at one time looked likely to dominate the web.

Using the web browser as an application runtime was critical for companies trying to bypass Microsoft’s desktop application dominance, and Google in particular invested in JavaScript, creating the V8 JavaScript engine for its Chrome browser, first released in 2008.

Then came Node.js, a server-side runtime for JavaScript based on V8 and first released in 2009 by Ryan Dahl. Dahl was particularly interested in improving concurrency in web applications, but another thing that drove adoption was that developers could use the same language both on the server and in the browser, and that JavaScript was an easy language to learn.

The mass adoption of JavaScript that followed also exposed its shortcomings. Microsoft’s Anders Hejlsberg, who also invented the C# language, believed that strong typing for JavaScript was essential in order to make large applications maintainable. He therefore came up with TypeScript, a superset of JavaScript with strong types that compiles to JavaScript for runtime compatibility.

TypeScript has proved wildly popular and most applications which target JavaScript are now written in TypeScript. If we add Scriptc or an equivalent into the mix, then the JavaScript element is all-but eliminated, though note that as currently designed Scriptc will run some of the code in a JavaScript engine so we are not there yet.

If you write in TypeScript and compile to native code though, why use TypeScript, why not use Go or Rust? Or if those languages are too demanding, Java or C# both of which have strong ecosystems?

The answer is the familiarity of TypeScript, the massive range of npm packages, and the ability to use the same language on the server and in the browser.

Nevertheless, the problem which Scriptc sets out to solve can perhaps be bettered simply by choosing a more performant application stack and reserving TypeScript for code that runs in the browser.

Getting started with Azure Artifact Signing

Signing applications is a requirement for deploying a desktop or mobile application and can be fiddly. Microsoft’s Azure Artifact Signing always looked to me like a good solution in that the pricing is reasonable at $9.99 per month for up to 5,000 signings, and it is run by Microsoft so one would think that the certificates will be satisfactory for Windows users.

This week I got started with the service. Microsoft has a handy quickstart guide and the first thing to note is that availability is limited to USA and Canada for individual developers, or USA, Canada, EU and UK for organizations. Luckily I work for a small UK limited company so qualify as an organization.

I also noted that a pay as you go Azure subscription is required; if you have a sponsored subscription that will not work.

The first step is to create an Artifact Signing Account. Billing starts from when this account is created, even if you are unsuccessful in obtaining a signing certificate. Should this be the case, make sure to delete the account.

Next, check the roles assigned to the user that will operate the code signing. There are two: Artifact Signing Identity Verifier and Artifact Signing Certificate Profile Signer. I assigned them both to my Entra ID user.

Now there are two steps to obtaining a certificate profile. The first is Identity Validation. In my case this is for an organization; you will need a DUNS number, two valid email addresses, a functioning web site, and the first and last name of the individual that will use the service. The organization address should match the one recorded at Companies House, even if day to day work is done elsewhere.

The organization identity verification automatically progresses to individual identity verification. In my case this was done by a company called AU10TIX and I was able to use a UK driving licence.

Next I hit what looked like a roadblock. I received an email asking me to verify my email address; however clicking the link got me a web page stating “the request is blocked” and “service unavailable”. Thinking that perhaps the service was unavailable, I tried several times and on different networks. I am not the first to experience this. I was on the point of raising a support case when I got a further email advising that validation was complete.

Once identity validation is complete you can create a certificate profile. The type I need is called Public Trust. I had several mysterious failures with uninformative errors; I suspect this might be to do with non-unique profile names but no idea really. Again I was on the point of contacting support when I tried a new profile name and it worked.

The quickstart guide ends at this point and the next document you need is the one on setting up signing integrations. You will need a recent Windows SDK including SignTool.exe, the .NET 8.0 runtime, and the Azure Artifact Signing client tools, if like me you want to sign a Windows executable.

Then you need to create a JSON file traditionally called metadata.json, and a command to sign the file. The command will be lengthy so fire up Notepad or similar and put it in a .cmd file or similar. You need to discover the location of SignTool and the location of Azure.CodeSigning.Dlib.dll and note that there are different x64 and x86 versions.

The command includes a timestamp without which the code signing is useless as the certificate has a lifetime of just three days.

I carefully assembled the command and I am happy to report that it worked first time.

Output from code signing command

So far so good. The whole process is somewhat intricate but I am hoping this will prove a good solution for Windows.

Kimi K3 and Qwen 3.8: is AI becoming a commodity?

Last week Moonshot AI introduced Kimi K3, with the promise that “the full model weights will be released by July 26 2026;” while Alibaba said on X that “Qwen 3.8 is launching and going open-weight soon”.

These are capable LLMs (large language models), and the open weights promise means that developers will be able to download them from Hugging Face and run them locally.

There are two reasons why local AI is inferior to pay-as-you-go cloud AI from one of the big providers: the LLMs are not as good, and the hardware is much inferior to data centres stuffed with massive amounts of RAM and GPUs.

However, there are also two reasons why local AI is much preferable to cloud AI: it is free to use, and more private. As an individual developer the notion of a constant trickle of spend while coding is disturbing, though I also see how the figures can make sense in a business context if there are corresponding benefits in productivity or staff numbers.

LM Studio using Qwen 3.6 to write some C# code

Joining the dots though, it seems possible that as local AI improves the premium (and uncertain) cost of what is sometimes called frontier AI will no longer be worth paying for.

Someone has to pay for the LLMs and there are political as well as technical aspects to this, as there are in other areas of IT. The implications of commoditised AI though are considerable, and means that we can expect further shifts in the tech landscape in this space.

No Mono in Microsoft’s cross-platform MAUI framework on .NET 11 – and a quick hands-on

Microsoft has released preview 6 of .NET 11, for which release is expected in November, including a big change to MAUI (Multi-platform App UI): it now runs only on .NET Core, rather than the Mono runtime.

Principal product manager David Ortinau reports that “your app builds and runs on CoreCLR, the same runtime behind ASP.NET Core, your cloud services, and desktop .NET.”

According to Ortinau, performance is generally faster than Mono on iOS and Mac Catalyst. The implication is that it is slower on Android, though he does say that it is within 10% on startup and app size. Selecting Mono is no longer an option, and the only use of Mono in .NET 11 is for Blazor WebAssembly.

There is a key remark in Ortinau’s post. “We have worked closely with first party .NET MAUI apps validating our progress, and have received feedback from several others,” he says. The question developers may ask: what first party .NET MAUI apps? Microsoft does not use MAUI for its well-known cross-platform apps such as Teams or Office. In 2023, the company said it uses MAUI for its 365 admin, Azure admin and Store Commerce apps, though I do not know if this is still the case.

Slide from .NET Conf 2023 showing some limited use of MAUI at Microsoft

Developers would have more confidence in MAUI, both for its quality and its long-term future, if Microsoft itself made more use of it.

That said, the company does seem to be putting substantial effort into MAUI for .NET 11. Along with the CoreCLR work there are other updates in this preview. Microsoft states that the focus in .NET 11 is “to improve product quality” and this is something developers will be happy about; in some cases a statement like this might imply neglect but for MAUI it is exactly what is needed.

I have a demo MAUI project and decided to upgrade it to the new preview, working with Visual Studio Code on a Mac. It was not as easy as I had hoped. The process involved not only updating the target framework, but also the minimum versions of Android, iOS and Mac Catalyst. Then the Android SDK had to be updated, with an annoyance related to the recommended:

dotnet build -t:InstallAndroidDependencies -f net11.0-android “-p:AndroidSdkDirectory=[your path to the Android SDKs]”

The command failed with an error about the Android SDK licenses not being accepted. The fix is to set an environment variable:

export AcceptAndroidSDKLicenses=True

and then it works, though I’m not sure how this satisfies the lawyers. I also had an error where

builder.Logging.AddDebug();

could not find the AddDebug method; the fix was to add a package reference to Microsoft.Extensions.Logging.Debug to the .csproj file.

With all that done, the “Start debugging” command is not working for me in VS Code. I get a message that the “configured debug type ‘coreclr_mobile’ is not supported, and an instruction to install coreclr_mobile Extension; clicking this button gets me “no extensions found.” Update – this was resolved after updating both the C# and C# Dev Kit extensions to pre-release versions (.NET MAUI was already pre-release).

However, dotnet run from the command line works:

Demo of MAUI using .NET 11 Preview 6 on a Mac

I plan to do some more experimentation; note that this is a preview of .NET so some friction is expected.

JetBrains Resharper for Visual Studio Code adds C# debugging support

One of the oddities of Microsoft’s Visual Studio Code is that it is free to use for almost anything other than the company’s own .NET platform. That is, there is nothing to stop developers from coding in C# with VS Code and using the .net command-line tools without paying Microsoft for a license; but the official C# Dev Kit extension requires a license for teams of 6 developers or more:

For personal, academic, and open-source projects, C# Dev Kit can be used at no cost. For commercial purposes, teams of up to 5 can also use the C# Dev Kit at no cost. For 6+ developers, those users will need a Visual Studio Professional (or higher) subscription.

In addition, there are licensing restrictions on Microsoft’s .NET debugger which impact VS Code forks:

The C# extension for Visual Studio Code includes the Microsoft .NET Core Debugger (vsdbg). Unlike VS Code, and most other parts of the .NET Core ecosystem, vsdbg is not an open source product but rather is a proprietary part of Visual Studio. It is licensed to work only with IDEs from Microsoft — Visual Studio Code, Visual Studio, or Visual Studio for Mac.

JetBrains has now introduced C# debugging support for the ReSharper VS Code extension, which means there is now another high quality option for developers who either prefer not to use the C# Dev Kit, or are using a VS Code fork such as AWS Kiro, Cursor, Windsurf or Google Antigravity, for which the official .NET debugger is not licensed.

ReSharper is free for non-commercial use, or costs £119.00 ($149.00) per year for an individual or £295.00 ($389.00) per year for developers working for an organization. The cheapest standalone Visual Studio Professional plan is currently $540.00 per year, though some developers will get it bundled with other plans.

Leaving aside licensing and cost considerations, how does ReSharper compare to C# DevKit and the Microsoft .NET debugger? I have yet to use ReSharper so do not have a personal opinion; but will note that Microsoft’s recent change which removed the Solution Explorer is unpopular.

Finally, my personal view is that Microsoft has more to gain by making .NET and its tooling fully open, than by playing games with licensing restrictions in an effort to keep developers hooked to Visual Studio or VS Code.

Fixing a .NET P/Invoke issue on Apple Silicon – including a tangle with Xcode

I have a bridge platform (yes the game) written in C# which I am gradually improving. Like many bridge applications it makes use of the open source double dummy solver (DDS) by Bo Haglund and Soren Hein.

My own project started out on Windows and is deployed to Linux (on Azure) but I now develop it mostly on a Mac with Visual Studio Code. The DDS library is cross-platform and I have compiled it for Windows, Linux and Mac – I had some issues with a dependency, described here, which taught me a lot about the Linux app service on Azure, among other things.

Unfortunately though the library has never worked with C# on the Mac – until today that is. I could compile it successfully with Xcode, it worked with its own test application dtest, but not from C#. This weekend I decided to investigate and see if I could fix it.

I have an Xcode project which includes both dtest and the DDS library, which is configured as a dynamic library. What I wanted to do was to debug the C++ code from my .NET application. For this purpose I did not use the ASP.Net bridge platform but a simple command line wrapper for DDS which I wrote some time back as a utility. It uses the same .NET wrapper DLL for DDS as the bridge platform. The problem I had was that when the application called a function from the DDS native library, it printed: Memory::GetPtr 0 vs. 0 and then quit.

The error from my .NET wrapper

I am not all that familiar with Xcode and do not often code in C++ so debugging this was a bit of an adventure. In Xcode, I went to Product – Scheme – Edit Scheme, checked Debug executable under Info, and then selected the .NET application which is called ddscs.

Adding the .NET application as the executable for debugging.

I also had to add an argument under Arguments passed on Launch, so that my application would exercise the library.

Then I could go to Product – Run and success, I could step through the C++ code called by my .NET application. I could see that the marshalling was working fine.

Stepping through the C++ code in Xcode

Now I could see where my error message came from:

The source of my error message.

So how to fix it? The problem was that DDS sets how much memory it allows itself to use and it was set to zero. I looked at the dtest application and noticed this line of code:

SetResources(options.memoryMB, options.numThreads);

This is closely related to another DDS function called SetMaxThreads. I looked at the docs for DDS and found this remark:

The number of threads is automatically configured by DDS on Windows, taking into account the number of processor cores and available memory. The number of threads can be influenced using by calling SetMaxThreads. This function should probably always be called on Linux/Mac, with a zero argument for auto­ configuration.

“Probably” huh! So I added this to my C# wrapper, using something I have not used before, a static constructor. It just called SetMaxThreads(0) via P/Invoke.

Everything started working. Like so many programming issues, simple when one has figured out the problem!

Microsoft to remove Azure “Basic” IP number and load balancer in favour of pricier options

Microsoft is removing some features from Azure which were called “Basic,” in favour of alternatives which have more features but are also more expensive.

A load balancer is a network component which balances traffic to virtual machines. The Basic load balancer is free but has a few limitations, such as no compatibility with availability zones, support for only 300 instances, no SLA (Service Level Agreement), and no support for NAT Gateway. Microsoft has emailed customers saying:

On 30 September 2025, Azure Basic Load Balancer will be retired. You can continue to use your existing Basic Load Balancers until then, but you’ll no longer be able to deploy new ones after 31 March 2025.

The Standard load balancer routes to availability zones, supports up to 5000 instances, is secure by default, and has a 99.9% SLA, but it costs $0.025 per hour, or around $18 per month, for up to 5 rules.

A Basic public IP number costs  $0.0036 per hour or about $2.60 per month. It is a perfectly good IP number but does not support zone resiliency. A standard public IP number costs $0.005 per hour or about $3.60 per month, and does support zone resiliency. A similar email has been sent to users, with the same dates.

Although these extra charges will not make much of a ripple in enterprise accounts, they can be noticeable, for example if you are an individual developing an application and trying to keep within a strict budget.

Web page memory usage: how much is too much?

I have a web application that loads names into a picklist and the question came up: how many names would be too much for the web page to handle? What about 5,000 names, for example?

Modern web browsers have a memory snapshot built in. Just press F12 and there it is. So I fired up my application with over 5,000 names loaded into an array and displayed in a scrolling div. 2.5 MB.

Then I visited Facebook. Logged in, the page reported over 78 MB.

image

Google’s clean-looking home page? Not logged in, 11.2 MB.

image

Twitter? Logged in, 83 MB.

image

This was enough for me to stop worrying about the memory impact of 5,000 names in my web application. With our casual acceptance of multi-MB web pages it is easy to forget that the complete works of Shakespeare in plain text is 5.5 MB and compresses to less than half of that.

Just because you can do something, does not mean you should. Smaller is better and faster, and software bloat of various kinds is responsible for many performance issues.

There are trade-offs though both in time and in performance. Maybe it is better to load just a few names, and retrieve more from the server when needed. It is easy to test such things. Nevertheless, I found it useful to get some perspective on the memory usage of modern web sites.

Installing Ubuntu 22.04 on Apple M1 with UTM

I  started with Arch Linux for Linux development  on  M1, which works, but succumbed to Ubuntu just because it is so widely used and therefore easier to find help. It is also supported by VS Code for remote development, as I am aiming for something similar to a WSL setup on Windows and using VS Code on the host side. I had problems installing 22.04 though;  the install completed but trying to boot resulted in:

EFI stub: booting Linux Kernel

EFI stub: Using DTB from configuration table

EFI stub: Exiting boot services

and there it would stay.

The fix I found was to update QEMU:

sudo port selfupdate

sudo port install qemu

after which Ubuntu started without issue (no need to reinstall).

Following this I was able to install and use the Remote – SSH extension in VS Code which worked first time.

Small points to note:

  • I accepted the option in Ubuntu to install the OpenSSH server during installation
  • In UTM I changed the networking for the VM to Emulated VLAN rather than Shared Network, in order to use port forwarding. I forwarded both the SSH port 22 and also the HTTP port 80, to different ports on the Mac, so that I can test web applications running on Ubuntu.

Thanks also to Liz Rice for her post here.