Cocoa

icon most likely not final

ScreenFloat 2 has officially entered “production”.
Apart from a bit of prototyping of various new features over the last couple of months and years, not a lot has happened in regards to ScreenFloat. But I feel now is the time to finally get it done.

ScreenFloat lets you keep visual references to anything you see on your screen floating above other windows using screenshots. It’s also a screenshot organizer.

Disclaimer: Estimated Time of Arrival, Pricing

I don’t do ETAs for my own products.
I’m a solo developer, I have multiple apps that need maintenance and updates, there are just too many moving parts for me to be able to estimate basically anything. And while that may be a serious lack of managerial skill: I accept that flaw and ignore it πŸ€·β€β™‚οΈ.

Regarding pricing, I don’t know what ScreenFloat 2 will cost yet. But I am resolved on its upgrade path: existing customers of ScreenFloat 1 will receive ScreenFloat 2 for free.

About this Journal

I thought it would be fun to chronicle my progress, struggles, successes, failures, struggles, failures, break-throughs, failures, and random stuff while developing ScreenFloat 2. That’s all.

Fundamental Decisions

Before I can start coding, there are a few decisions I have to make in order to be clear on where I want to go and what I want to achieve.

Decision 1 – Build on ScreenFloat 1’s code base, or start from scratch?

I began work on ScreenFloat 1 on March 11th, 2010.
Memory is still managed manually (for you youngster coders out there: google retain / release to know what I’m talking about).
Objective-C’s @property wasn’t even available yet back then.
It’s ancient!
In addition, it was one of my first apps, so ScreenFloat 1’s code is all over the place. And while I don’t think code has to be “pretty”, I do think it has to be readable and understandable; ScreenFloat 1 is neither.
So yes, I’ll definitely be starting ScreenFloat 2 from scratch.
Which my next decision factored into…

Decision 2 – Objective-C, or Swift?

Swift, of course. I love Objective-C (it’s got me this far), but I think by now it’s clear that the future is written in Swift. That’s not to say that if there wasn’t Swift, I wouldn’t love to continue working in Objective-C. However, with more and more frameworks being Swift only (for example, Widgets), I need to move on as well.
While this decision is of no consequence to users of the final product – a feature in an app should work no matter what language was used to program it -, it is quite consequential to me.
I began learning Swift earlier this year – my freeware developer tool BackLog is a first result of that – and I’ll continue to learn. For me, the easiest way to do that is “on the job”: to actually work on something I’m going to ship. Two birds with one stone.
Will that increase development time? Possibly.
Is it worth it? I believe so.

Decision 3 – Keep old storage model, or migrate to Core Data?

ScreenFloat 1’s “storage model” was two plist files (one for the shots library and associated metadata, and one for categories the user created in the Shots Browser) and a folder full of image files.
It worked ok, but I’d like something more sophisticated, robust and scalable.
That sounds like Core Data to me.
It’s not a new framework to me (thankfully – it is a lot to learn). I’ve been using it for a couple of internal tools, and for apps I’m maintaining for third parties.

As a side note, I do want to keep the “folder full of image files”. It makes them accessible in Finder, instead of being stored somewhere on disk in an opaque Core Data storage file.

Decision 4 – Core Data with CloudKit, or Core Data with custom iCloud/CloudKit sync?

I recently took to twitter to see what other developers thought about Core Data with built-in iCloud sync. The consensus was pretty much to stay away: it can be slow, very opaque as to what it’s doing, and thus difficult to debug.

Synchronisation is difficult to debug as it is, so I don’t want to make it any harder than it has to be.
Over the years, I’ve gained quite a bit of experience when it comes to syncing with iCloud / CloudKit (I manually sync with iCloud using CloudKit in Yoink for iPad and iPhone and Transloader), so I’m confident I’ll be able to write my own custom CloudKit sync solution for ScreenFloat 2.

Decision 5 – macOS, sure, but what about iOS/iPadOS?

ScreenFloat 2 will be available for Mac, as well as iOS/iPadOS.
So some code (most notably storage and sync) has to be able to run on all those platforms – something to consider going forward.

As with all my other apps, ScreenFloat 2 for Mac and ScreenFloat 2 for iOS will be developed for and tailored to each respective platform mostly separately, and so they will also be sold separately.

That’s it for this time.
Thank you for joining me. Feedback, input and questions are welcome: mail me, tweet me.
Take care! πŸ€—

Read more

Yoink v3.6.5 re-introduces its clipboard history feature and the accompanying widget.
Here are a few details about its implementation for macOS Big Sur and newer.

The No-Button-Action Conundrum

Widgets on macOS Big Sur and up (and iOS, for that matter) can’t really react to a user’s click on them by themselves, so you cannot run any logic from a user’s input.
A click onto a widget will always bring you back to its containing app, where you can then run logic accordingly.
On iOS, that’s obvious, as it’ll open up the owning app and put it front and center.
On macOS, that can be a little more subtle, because macOS allows apps to run in the background (like Yoink does, mostly).

When you click on an item in Yoink’s widget, it’ll tell Yoink to run the logic to copy the clicked item (or pin it, or add it to Yoink, or reveal it in the browser, depending on the modifier key you pressed during the click).

But there’s a problem with that in the case of Yoink:

The Foreground Conundrum

No matter where you click on a widget (either the background of it, or a SwiftUI Link() object), it will bring its containing/owning app to the foreground.
For many apps, that will be fine. But Yoink is an app that runs in the background and only very rarely needs to actually become the active, keyboard-input-accepting app.

So I thought, perhaps I can have another application inside Yoink’s app bundle, which is LSBackgroundOnly, with a custom URL scheme that would be called from the widget using a SwiftUI Link() object. An app that has LSBackgroundOnly set to YES in its Info.plist cannot present any user interface, and cannot become “key”, even if the system tells it to.
But there’s another roadblock – even though only the secondary app inside Yoink’s app bundle should be able to open the custom URL scheme, the widget will send that Link() to its containing/owning app only, no matter the url scheme. In this case, the main Yoink app.

The point of the widget is to quickly re-copy something and then paste it somewhere right away.
Say you're editing text in TextEdit, then open the widget, click on it to re-copy something and then press command-v to paste it into TextEdit only to hear a beep because the app isn't active anymore (because Yoink is). So you have to click into the TextEdit document to make it active, and only then can you paste. That can (and will) become annoying very quickly.

In order to have Yoink never become the active app from a click on its widget, I’d have to move the widget plugin/appex bundle out of the Yoink app bundle’s PlugIns folder, into the secondary target one’s.
Only that way would the widget attempt to make the secondary target(and thus, not Yoink) frontmost – and fail because of LSBackgroundOnly, leaving the currently frontmost app active – and send the custom URL scheme link to it. The secondary target would then forward the link to the main Yoink app bundle.

Tada, it works. But that lead to yet another problem. (When did “it just works” turn into “it just won’t”? And while I’m at it, what’s with those widgets? They feel quite… neutered to me compared to what they were able to do before.)

The Widget Recognition Conundrum

So far, I managed to have Yoink not become active when an item in the widget is clicked.
But now, with the Yoink app containing the secondary target containing the widget, macOS was unwilling to recognize and show the widget in Notification Centre.
Interestingly, a double-click onto the secondary target in Finder would make it show right away.
Infuriatingly, launching the secondary target quietly from within Yoink at launch won’t – and I had a lot of different approaches:
– Launching the secondary app directly with NSWorkspace’s -launchApplicationAtURL:…
– Launching the secondary app via its URL scheme
– Having Finder open the secondary app
– Running an NSTask open -a <secondaryapp> /path/to/somefile
– Registering the app using LSRegisterURL

None of it worked. I figured, the problem was that it was Yoink launching the secondary app, not the “system” or “user”, like it was the case when double-clicking it in Finder.

Which made me think of login item helper apps.
Inside the macOS app sandbox, an app cannot set itself as a login item. It needs to have another “helper” app inside its Library/LoginItems folder which it designates as a login item, and that app will then in turn launch the containing app. Nuts, but there it is.

The point is, the system launches those login item helper apps, not the containing app.

Heureka. In Yoink, at launch, I set my secondary app, which contains the widget bundle, as a login item with SMLoginItemSetEnabled, causing the system to launch it. Now, finally, Notification Centre recognizes and shows Yoink’s widget.

What a needless journey.

Read more

I’m happy to announce the release of a new freeware app: BackLog.

The premise is simple:
It’s complicated, or at least cumbersome, to get logs that predate you clicking “Start streaming” in Console.app – Apple’s recommendation for it is to *create an entire system diagnostics report* and look for files in there. Madness.
I wanted a quick and easy way to, for example, get all log entries from all processes that happened from boot time to 5 minutes after. And that’s what this app lets me – and now, you – do.

Hello BackLog
BackLog, showing all log entries from boot-time to 4 seconds after boot-time.

Using BackLog is pretty straight-forward. Select the process you’re interested in, specify a time-range and an optional text-filter and hit Load Logs.
You can then select it all and copy-paste it somewhere, or hit the little Share button at the lower left to share it as a file.

For convenience, right-click the ‘from’ or ‘to’ date pickers and select a time from the contextual menu, like Boot time.
What makes it a Developer Tool?

While diagnostic logs might appear to be of interest mostly to developers, a simple logging-app is not a developer tool to me, per-se.
What really makes BackLog such a tool, in my opinion, is that developers can create backlog:// links which they can send to their customers, and, when they have the app installed and click that link, all the options (like process, date range and message type) will be set for them beforehand – no hassle. All the customer has to do is hit Load Logs and send the results back to the developer.

Numerous times I’ve had to deal with obscure app-sandbox or keychain access issues in Yoink, and having to tell customers to please open Console, filter for Yoink, and then (hopefully) reproduce the issue is just bad UX.
Now, I can send them this app with a backlog:// link, with a time range pre-defined, and all they have to do is copy-paste the results into a response to my mail. And best of all – they don’t have to reproduce anything, the logs already contain all the info I need from the last time the issue occurred.

You can either copy only the backlog:// link, or have it include pre-written instructions for your customers.
Links and Further Info

BackLog is and will be Freeware. If you like it, however, I’d like to ask you to take a look at my other apps I offer for purchase on the App Stores – thank you πŸ™‚

The app requires macOS 10.15 or newer and is localized – for now – in English and German.
It’s sadly not sandboxed, because the OSLogStore APIs won’t work in the sandbox environment πŸ€¦β€β™‚οΈ.
But Hardened Runtime is enabled, and it’s been notarized by Apple.
Naturally, it runs natively on both Apple Silicon and Intel Macs.

For BackLog to work reliably, the app must be run from an admin account, or using ‘sudo’ (user-discretion advised)

Website
Direct Download (801KB, zipped)

It’s also my first app completely written in Swift. That doesn’t make the app any better or worse. It’s just a fun fact.

If you have any feedback or questions, you can reach me any time by eMail. I’m looking forward to hearing from you.



Enjoy πŸ€—


Read more

Just a quick update on SiriMote, as I’ve been putting out a few updates for it over the last few weeks.

The app now has improved support for controlling the following apps with the Apple TV Siri Remote:
– Boinx Software’s FotoMagico
– Kodi
– Apple’s Keynote
– Apple’s TV App
– Infuse 7

SiriMote is freeware, and you can download it here.

Enjoy πŸ€—

Read more