Software Development

In lieu of a software update today – ScreenFloat v2.3.8 is taking me a bit longer than anticipated – I thought I’d share a fun 🙃 story about SwiftUI with you.

ScreenFloat offers widgets that give you quick access to different things.
Among others, you have the Capture widget which allows you to start a screen recording, capture your screen, import from your iPhone, etc.
And there’s the Recent Captures widget displays your recent shots so you can easily float them.
Or so I thought. But I’m getting ahead of myself.

The Capture widget
The Recent Captures widget

All the actions in these widgets (when a button or a shot in a widget is clicked) are powered by SwiftUI’s Link(), which opens my app with a custom URL that contains whatever information I need to perform the action the user requested.
For clicked shots, that would be floating them (such a custom URL could look like this: esssf2://widget.at?shotID=<someid>&action=float).

Now, some of these widgets are static (the capture utility widget, for example, can not be configured in any way by the user and always shows the same capture buttons), while others are dynamic (widgets that display shots can be configured to only show images, or only video recordings, for example).

Configuring the dynamic Recent Captures widget

That’s all lovely, but recently I discovered something puzzling.
While my static widgets worked just fine – each button in the Capture widget was individually clickable and did the correct thing in my app –, clicking a shot in a widget to float it would not open my app with the custom URL I specified, but with the dynamic widget’s configuration intent instead.
No matter which shot I clicked, it always did the same, wrong, thing.
(A configuration intent is what internally makes customizing widgets possible; and to make things even more confusing, ScreenFloat’s dynamic widgets are set up internally partially through a .intentdefinition file (Apple’s older way to make widgets configurable), and partially through AppIntents (Apple’s modern approach) ).
So instead of my app receiving the correct custom URL which tells it which shot to float, it receives info on how the widget is configured – information not exactly useful in this context.

So I figured it had to do with the widget’s dynamic nature – a logical assumption, given that my app received the configuration intent instead of my URL.

First, I thought perhaps the .intentdefinition file and the AppIntents somehow confused each other, so I temporarily removed the definition file, but the results were the same. Dead end. (Which is good. Otherwise I would have only been able to offer my widgets on macOS 14 “Sonoma” and later, leaving behind two earlier releases of macOS that ScreenFloat supports).

So I set out to test my assumption that the widget’s dynamic nature was at fault.
I tried yet another dynamic widget, the Folders widget.

The Folders widget

It can be configured to show all, only smart, or only “dumb” folders, and when clicked, ideally opens the clicked folder in ScreenFloat’s Shots Browser.

With a smirk on my face, confidently clicking on a folder in that widget, absolutely certain it would not result in the folder being opened in ScreenFloat’s Shots Browser, what did my tired, shocked eyes perceive?

The freaking folder opened in the freaking Shots Browser! The Link() with my custom URL worked!

 

But why!? I couldn’t make sense of it.
What was the difference between the Link() in my Recent Captures widget, and the Link() in my Folders widget?
Readers dabbling in the arts of SwiftUI might have already noticed that the first consists of Image()s, while the latter is made of Label()s. So I swiftly swapped out the Image() for a Label() to test that this was indeed the culprit, and lo and behold, the widget now worked.
A shot was finally floated correctly!

 

At this point, I was desperate. Close to tears of rage. Ready to rage-quit. Not only Xcode, but also my app’s development. My profession. Coding in general. I was done.
Perhaps Image()s in Link()s are just bugged and there’s nothing I can do about it?
The thing is, I knew it worked at some point, so I fired up my virtual machines in UTM and went back to see when it started to break.
macOS 12? Fine.
macOS 13? Fine.
macOS 14? Fine.
macOS 15? Bingo.

Something on macOS 15 and later broke my widget.

So the first thing I did was to remove all modifiers from my Image() and try again. If it still didn’t work then, I’d have to find a different way to make it work. Maybe hack my own Image() implementation together. Fun times ahead.
But I got lucky for once. Removing all modifiers from the Image() did the trick.
Now all that was left to do was to go through each modifier and see which one broke things.

“How many lines of code are there?”
“About two million…”

Ready?
.widgetAccentedRenderingMode(). It was .widgetAccentedRenderingMode() that messes with Link()s.
Only specific configurations, though: While .fullColor and .accented work fine, the other two (.desaturated and .accentedDesaturated) render the Link() defunct. Guess which ones I used.

Now why is this happening? Nobody knows. I doubt Apple knows. Or cares.
Is any of this documented? LOL.
How long has this bug been around for? Since macOS 15, so for about 2 years.

Now that I knew where the bug was, out of interest I googled it, and sure enough found others with the same issue. Not something I found before, because who’d think to google for that particular setup? Nobody, that’s who.

So, there you have it. A Link() containing an Image() with .widgetAccentedRenderingMode() set to .desaturated or .accentedDesaturated will render said Link() defunct 🤷‍♂️ .
I found two ways around this: Don’t use those two properties, or don’t embed the Image() in the Link(), but have the Link() be an .overlay() modifier of the Image(), like so:

var body: some View {
    Image(...)
        .overlay {
            Link(destination: ...) {
                Rectangle()
                    .foregroundStyle(.clear)
                    .frame(...)
            }
        }
}

The downside with the .overlay{} method is that you lose any visual click-feedback, where the clicked Image() would change color on mouse down.

The Shot floats when I click it in the widget!

A radar’s been filed (FB23593239) and is now ready to gather dust in the forgotten depths that is Apple’s Feedback Assistant, never to be looked at again, until some digital archaeologist far into the future stumbles upon it and finally marks it as “works as intended”.

I’ll go now and scream into a pillow some more.

Update July 7th, 2026:
I was contacted by a very nice person on the SwiftUI team at Apple notifying me that A) I posted the wrong radar number above (fixed now, mea culpa), and B) they’re looking into it. That’s amazing, I really appreciate that. Thank you, J. and team. I humbly redact my snarky statement about Feedback Assistant.


Read more

source: apple.com

If you’re getting an error trying to install the macOS 27 beta ipsw from developer.apple.com/downloads/os in UTM…

error installing macOS 27 directly in a UTM VM

… I have a solution for you.

Some recommend downloading Xcode 27, installing it, and installing its MobileDevices.pkg and maybe CoreTypes.pkg and a bunch of others. Some report it works, others that it doesn’t.


Here’s what worked for me, on an M1 Max MacBook Pro with macOS 26.5.1:

 

  • Download the macOS Tahoe 26.x ipsw
  • Install it into a new VM in UTM
  • Log in to your developer account in System Settings
  • Update to macOS Golden Gate 27
allowing beta updates in System Settings, source: 9to5mac.com
the upgrade is downloading
the upgrade is installing
macOS Golden Gate 27 is installed and ready to use

Hope it helps : )

Apps | Contact & Connect

Read more

I’ve been wanting to make my website a bit more “alive” for a while, and I finally had the time to do something about it.


App’s Prices

For the longest time, I didn’t show the prices for my apps on my website (crazy, I know), simply because I didn’t feel like going into the HTML source, updating the static price, and re-uploading the page every time I had a sale.

Now, I have a PHP script that takes care of it for me. It can load prices for different localities using the App Store API, or from Paddle. I currently just get them from Apple right now, because my apps’ prices are the same on the App Stores and my website. The results are cached for 6 hours so I don’t have to query the API for every page load. For time-sensitive sales, I can easily discard the cache and have the page (and thus, prices) reload for everyone instantly.

Prices are now shown on the main website (see above), and on each app’s webpage:


“Current Version” Indicator, Release Notes

For ScreenFloat 2.0, I built an entire API and database with PHP and MySQL for the release notes I show in-app:

It allows me to get all sorts of information about any release, the obvious ones being version, release date and download file size.
This powers my in-app update mechanisms, the in-app release notes you can see above, and now the “current version” of apps on my website (see below), along with a web version of my apps’ release notes.

A click onto the version brings you to the web release notes. I tried to replicate the in-app release notes as closely as possible, with image- and video previews of features, etc. You can switch between English and German, rich- and plain text, and browse through the release history.


Highlighted Blog Posts

Though I’ve always linked to my blog from my website, I thought it would be neat to feature the newest blog post on the main page and the latest app-related post on the app’s webpage. I hope this is more enticing to click than a simple “Blog” link.

This uses the basic WordPress API. It looks for posts with specific tags or within specific categories, published within the last 2 months. If there aren’t any blog posts with that criteria, nothing is shown. I thought that better than showing old, outdated posts. I don’t want to give off “deserted” vibes.


Friends Page

I have a new Friends page where I highlight a few long-time friends of Eternal Storms Software. I still need to find a way to include that into the main website better.


Auto-Applying Discount Codes on my Web Store

Probably a no-brainer for anyone else, but I coded my web store myself, and only for Black Friday 2025 had the idea of auto-applying discount codes. That way, everyone gets the benefit of a discount when a sale’s going on, even if they don’t know about it.
The way it works is, I have a specific naming scheme for site-wide and app-specific discount codes, and the web store looks for them and applies them automatically.


Tip Jar

Recently, I’ve been implementing Tip Jars into my apps, for anyone who’d like to completely voluntary further support my work, beyond the app’s one-time purchase price (I don’t offer subscriptions).

Tip Jar in Yoink for Mac

Since I implemented it for both the App Store and via Paddle for the direct-purchase versions of my apps, I figured I’d implement a small web version of the latter, too.
I’m linking to it from my blog, but I’m still undecided about adding it to the main page. Probably going to leave it out.


Things Still In the Works

For now, there are two things I still haven’t gotten around to including.

Dynamic App Reviews

I do include reviews from the App Stores on the main and app pages, but they’re static. I’d like it to be a bit more dynamic, where I’d show random 4- and 5-star reviews. Apple offers APIs for that, so it shouldn’t be too much of a hassle.

A “Sources” Page

I’d like to have a page up indicating official sources for my apps: my website, the App Store, Bundlehunt (on occasion), MacUpdate, etc.
Basically a single source of truth for figuring out if a website offering a download of or licenses for my apps is official and legit.


Now back to work. Since I’m not vibe-coding, my apps don’t develop themselves!


Read more

I am impulsive sometimes.
Like recently, when I came across this reddit post by chance and thought to myself: I could write this. And so I did; and before I knew it, I had a new app on my hands.

This is a little behind-the-scenes recap of the app’s inception.


Researching existing apps

I didn’t do any research of other apps – I never do when I set out to write a new app.
Why? I’m not sure. I feel like I’d get put off creating my own thing when I know there’s already a few options out there. Or that my idea might get diluted by what other apps are doing, swerving me off my original “vision” for the app.

And in the time it takes to research existing apps and their functionality, I could whip up my own tech demo – time much better spent.

So I skipped all that boring business and market research stuff and just did what I love doing most: code.
The market’s huge, and it will take care of the rest by itself – people either like the app, or they don’t. And that’s the case even if I spend a day researching existing solutions.


Proof of Concept

I knew what I wanted: an app that covers your desktop with the click of a button with your current desktop wallpaper.

Now, there’s this “neat” API on macOS that, on first glance, appears to be made for this: NSWorkspace.shared.desktopImageURL(for: ).
But it’s completely useless.
Maybe it actually worked for a short while after its introduction with macOS 10.6, but it’s obvious nobody at Apple ever took another glance at it again after that, ever.
Instead of returning the URL to the currently used wallpaper image file, it sometimes returns the wrong image file, or a URL to the folder containing the image file… I just couldn’t get it to work reliably – especially when a dynamic wallpaper, or a shuffling wallpaper is set, it just straight out poops its pants.

But that didn’t bother me. In fact, I approached the entire thing from a different angle, that’s much better suited to the needs of the app.
With my experience in ScreenCaptureKit and general screen capturing and window mechanisms in macOS (because of the work on my app ScreenFloat), I immediately had some pseudo-code in my head, so when I sat down to write the app, I knew how I was going to approach it.

If you’re wondering what ScreenCaptureKit has to do with your desktop’s wallpaper: absolutely nothing. But it’s a means to an end in my case.
Basically everything on your screen is a window: your menu bar, each icon in the right portion of your menu bar, the desktop wallpaper image, the files and folders on your Desktop, the widgets on your Desktop, your actual windows…
And if it’s a window, it can usually be screenshotted. That’s where ScreenCaptureKit comes in.

All I had to do is find the correct window (the one that displays the wallpaper) to take a screenshot of, then find out the window levels of the Desktop and the Desktop’s files and widgets, and then create a new window with my screenshot of the wallpaper as its content and place it delicately over the Desktop, Desktop files and Desktop widgets, below any app’s windows, and I’m set.

Luckily – and to my genuine surprise – that’s exactly how it worked out. The proof of concept took maybe half a day. Now came the actual work.


The Actual Work

… by which I mean putting it all together into a nice app.
Obviously, I wanted users to be able to select a custom wallpaper, or a solid color, or just blur the background, in addition to their currently set wallpaper – that’s just a given for such an app.

I also wanted users with multiple screens to be able to cover Desktops individually. Maybe they want to keep one Desktop accessible for files, while covering up the other one they screen-share, for instance.
With that I had to make sure that DeskMat reacts correctly when screens are disconnected or connected to the Mac.

I also had the idea of Auto-Triggers, where DeskMat would automatically cover the desktop when user-specified apps are launched, or come to the foreground. One gotcha here was that I did not want Auto-Triggers to interfere with a cover the user activated manually on for a Desktop, so I added an activationSource to my cover windows to know what triggered it – a user, or an auto-trigger (or, in v1.1, a Focus mode).

There’s also a lot of busy-work involved with a new app.
For my apps, that involves setting up a release notes database to display what’s new, update checking, update-installing for the website-bought version, license creation on the server and handling in the app for the website-bought version, setting up the app in the Mac App Store, having a nice About window, creating a website…
All this took longer than developing the actual functionality of the app, which ultimately spawned an internal project I call ESSLibrary – a Swift Package which, going forward, will make it so much easier to implement all that stuff with just a few static variables in a plug-and-play system. But that’s another story.

So, after maybe 3 days of creating the core app came another three-and-a-half days (!) of setting up the app’s infrastructure. What a pain (and, hopefully, with ESSLibrary, a thing of the past).

I wish I could have supported versions of macOS older than Sequoia 15.0, but due to certain API only available on 15+, it would have been too much work to have it support older versions, for such a small app.


The Icon

I didn’t want to spend, to be frank, any money on a professionally designed icon, so I “designed” it myself.

It’s a Terminal.app-like frame, with Sequoia’s standard desktop wallpaper with file and folder icons, and a “protective cover” on top of it – the kind you’d put underneath your desk chair so it doesn’t ruin your hardwood floors – which was the actual inspiration for this. I know, it’s a floor mat, then, not a desk mat, but I figured… what the heck.
For comparison’s sake, here’s my IKEA floor mat:

I think it turned out okay. I do like the grainy / noisy texture of the cover in the icon and I feel it conveys what the app does nicely.

A small tidbit: Blurring your Desktop with DeskMat (in addition to having a wallpaper image cover) wasn’t originally planned. But because the cover in the icon does blur it, I thought it could be conceived as misleading if the app didn’t actually offer a way to do that, so I went back in before release and added that option.


Trial Limitations

I like to be lenient with my apps’ trials. I do have a 28-days time limit, that’s true, but I also grant an additional week of trial time every couple of months (and for every update) so that people who tried the app before but didn’t like it (or had an issue with it) can try it again after a while and see if it works for them now.

I’m torn on doing a freemium app on the Mac App Store (my apps are paid up-front, with a notice that there’s a free trial on my website). I just dislike the look of “free with in-app purchases”. It feels like I’ve got something to hide.

How much does this actually cost? You won’t know until you scroll all the way down to the obscure and easy-to-miss “In-App Purchases” field.

Instead of “Get”, it should say something like “Free trial, then x,xx €”.

It’s clear up-front how much this app costs.

Ah, much better. This immediately tells me that when I pay for the app, I get to use it (or at least that version) indefinitely, without limitations.


Pricing

My apps are one-time-for-life purchases (with optional tipping for those who feel so inclined), and I didn’t want to change that with DeskMat. I had originally planned to sell the app at a discount for EUR 3.99 and then, two weeks later, raise it to EUR 5.99, but because it was so well-received at that discount, I decided to keep it for the time being.


Publicity

Like I usually do with new apps and bigger updates, I sent out press releases to all the major Mac news publications and posted here and there. Being a small kind of niche app, I did not expect a lot of coverage, but it did fairly well, especially in German publications.
Here’s a little selection:

Macwelt (de), MacGadget (de), Techgedöns (de), ifun (de), iTopNews (de), MacSources (en), MacGeneration (fr)

All in all very favorably reviewed.

It also got featured on the Mac App Store in “Apps and Games We Love Right Now”…

… and in “Great New Productivity Apps and Updates”

And, in Austria 🇦🇹, it even reached the #1 spot in Top Paid Apps! (which means about one person bought it here 😛)


Customer Reception

Like I wrote above, reviews by publications have been very favorable. As was the feedback I received by mail from customers. Commercially, the app has done well so far – I didn’t expect too much from it, but it exceeded my expectations quite a bit.

Comments on articles and on reddit, however, have been quite the opposite, interestingly:
From “who needs this”, to “who uses their Desktop anymore”, to “this is a scam” (somebody gift this person a dictionary) and “AI can write this in five minutes” (I’d *love* to see them try, ’cause … AI … lol) and “app x can do this and more” (fair enough, a valid point for once), comments have been generally negative.

I attribute some of this to the times we live in. Seems like people love to tear down other peoples’ efforts while not producing much – or anything – themselves.
And hating is just so much cooler (and faster) than giving constructive feedback.

To vent a little bit: I don’t get it. Personally, I’ve never felt inclined to leave a “who needs this” (or any plain negative) comment on anybody’s work. Okay, so you don’t need this app. Fine. But there are others who might, and the app wasn’t written with only you in mind. Who needs this “who needs this”-comment, is what I’m saying.
And don’t get me started on the “this is a scam” comment. If you use a five-dollar word like “scam”, you damn well better know what it means.

But let’s not end on a negative note.
I like how the app turned out and that people who actually tried and even purchased it seem to enjoy it – and I want to thank you all for it. I love little side projects (i.e. distractions) like these : ), especially when they turn out useful.


DeskMat is available as a one-time purchase directly from my website, and on the Mac App Store.
It is currently available in English and German and requires macOS Sequoia 15 or newer.
It costs €3,99 / USD 3.99 / GBP 3.99
A free, 28-day trial is available on the website.

DeskMat Website + Trial + Direct Purchase
DeskMat on the Mac App Store
DeskMat Trial Download
Get to Know DeskMat (User Guide)

Contact & Connect


If you’d like to get in touch, you can reach me by mail (or by using the Contact & Connect link above).


Read more