Software Development

icon still not final

Core Data is like conversation: Take things out of context, and eventually, it will all come crashing down on you.

Me, in an attempt to begin this entry with something witty

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.

Entry 2 – Core Data

This week, I’ve been busy with getting ScreenFloat 2’s migration to Core Data going and working, the first task being to decide on how I want to handle Core Data in the first place.

No multiple contexts, no problems. Right?

It’s been more or less established that, at the very least, you should have two managed object contexts when working with Core Data: One on the main queue for UI work, and another one on a background queue, for fetching and other “work”, so it doesn’t block the UI of the app.
This can make things tricky, because an object obtained on a background context should not and can not be used on the main queue to update the UI (and vice-versa). It has to be – for the lack of a better word – “converted” to a main queue object so that it can be used there.

So when I began working on migrating to Core Data, I thought, what if I could have the best of both worlds, with only one main queue context? Wouldn’t that solve all my potential problems?

John Hammond: “I’m not making the same mistakes again!”
Ian Malcolm: “No, you’re… making all new ones.”

Jurassic Park 2: The Lost World

What if the ScreenFloat app wasn’t aware about its Core Data nature at all? I could do all the Core Data stuff in a separate XPC service, and ScreenFloat would communicate with it to retrieve and update objects. The XPC service is its own process, so doing things there on the main queue context wouldn’t block the UI of ScreenFloat at all.

Sounds too good to be true? That’s because it is.

Issue #1 – Abstraction and Maintenance

To communicate with the XPC service, I’d have to create what I call “translator” classes that I send back and forth from and to the XPC service – classes that ScreenFloat understands (because it wouldn’t know about Core Data entities/objects), and the XPC service understands (so it can “translate” changes back to the Core Data store).
That would result in more work and maintenance. Anytime I wish to update a property, I would not only have to update the Core Data model and its entities, but also these custom “translator” classes.
And what about relationships in the Core Data model? Those would be a pain to handle this way.

Issue #2 – Bloating

ScreenFloat 2 will not only consist of the main app, but also a Share extension, at least.
That would mean to have to have this XPC service available to it as well, which in turn would mean having the XPC service inside the app twice – unnecessary bloat.

Issue #3 – iOS

iOS does not offer XPC services to 3rd party developers. But I want to have the same way of handling Core Data on all platforms ScreenFloat will support (I don’t want to have to do basically the same work twice), so it’s another no-go.

It was nice dreaming and wondering about this, but in the end, the more traditional approach to Core Data is still the best in my case.
Now I have a background queue context – used only for saving to disk -, on top of that the main queue context – used for updating the UI -, on top of that another background queue context – used for actually retrieving and editing stuff -, and on top of that an optional background queue context for work I might want not to persist/save, so it’s easy to discard. It works well so far.

Migrating ScreenFloat v1’s database to v2

I figured, the best way to get Core Data set up, working, and to see if my implementation works correctly, is to code it on-the-fly, adapting and changing it as needed. The best opportunity for this was to work on the migration of ScreenFloat 1’s dual-plist-file-based library. It’s the perfect case to see if my contexts are set up correctly, if conversion from one context to another works as expected, if all data and relationships are populated correctly, if it performs well, and if it saves to disk correctly.

On my MacBook Pro M1 Max, it imports the 699 Shots that currently reside in my ScreenFloat 1 library in less than 0.5 seconds (copying the files to their new destination, reading the plist files, creating the Core Data objects, and saving to disk).

It’s so fast that I had to slow it down artificially, so that the user can at least read that it’s upgrading the Shots library. If it weren’t for that, the progress bar would just be a weird, flashing artefact in the UI, leaving the user clueless as to what just happened. So instead of starting the migration process right after the Splash Screen appears, there’s a little delay, and then the process starts. When it finishes, there’s another short delay, and the Start using ScreenFloat button appears.

Migrating the library also got me thinking about what additional (meta)data I might need further down the road. For instance, ScreenFloat 1 doesn’t do “favorites” for Shots. So an “isFavorite” property would be a good idea.

Another one’s a bit more complex:
Users can put Shots into Categories to organize them. I’d like to store a bit of contextual information for that. For instance: when was this shot added to this category?
If a Shot could only be added to one category, that would be easy – just have an optional “dateAddedToCategory” property. But categories have a multiple-to-multiple relationship with Shots: a category can contain multiple Shots, and Shots can be added to multiple categories.
To my knowledge, the only way to do that is with another Core Data entity (let’s call it “CategoryShotMetadata“) and a many-to-one relationship to both the category and the Shot. When a Shot is added to a category, it automatically creates an object of this metadata entity, populates its “dateAdded” field, and sets up the relationships to the category and the shot. This makes it easy to fetch later: the Shot-to-metadata and Category-to-metadata is a one-to-many relationship (a Metadata object has one Shot and one Category, but a Shot/Category can have multiple Metadata objects). Now, the metadata object can be identified by the combination of its single category and single shot relationships.


I’ve been enjoying my time with Core Data so far (and I’m fully aware that this might change at any given moment πŸ˜‰ ) . It’s definitely more complex than my previous two-plist-file-approach, but – let’s be frank – Core Data is just better.

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

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

With Yoink for Mac‘s clipboard history working again on macOS Big Sur and newer, I’ve seen, in forums and such, some questions about how the clipboard history operates and what it stores, in regards to privacy. I’ve answered those questions, but figured I’d let everyone know about it as well here on my blog, since this *should* be publicly available info:

General Notes about Yoink’s Clipboard History

  1. By default, the clipboard history feature is disabled.
    It has to be manually enabled by either clicking onto the widget in Notification Center, or in Yoink’s preferences, under Extensions.
  2. The clipboard history feature can be disabled at any time (and will clear any stored items at that point) in Yoink’s preferences, under Extensions.
  3. Individual items can be deleted in the Clipboard History browser, accessible by command-clicking onto an item in the widget, by selecting Clipboard History > Organize… in Yoink’s contextual menu, or by clicking Organize… in Yoink’s preferences under Extensions.

What the Clipboard History stores

By default, Yoink stores anything you copy or cut, be it some text from a document, an image on a website, or a file in Finder, for example. Please read “What the Clipboard History does not store” below for important exceptions to this.

The clipboard history can be configured by you to completely ignore copy/cut operations in certain apps. This can be done in Yoink’s preferences, under Extensions, by pressing “Ignored Applications: Edit…”

The clipboard history is stored locally on your Mac and does not leave your Mac, unless you do it manually.

What the Clipboard History does *not* store

Yoink completely ignores cut/copy operations from any app or process that has one of the following in its name:
Keychain, Enpass, 1Password, KeePass, LastPass, Password, Kaspersky, mSecure, AppLocker, Keeper Password, Passwort, oneSafe, Secrets, Strongbox, RememBear, Dashlane and Bitwarden.
Anything copied from an app whose name contains one of the above (case insensitive) does not get stored in Yoink’s clipboard history.

In addition to that, Yoink also ignores copied content from any app, if the resulting clipboard content contains any of the following data types (as suggested by developers, for developers, on nspasteboard.org):
com.agilebits.onepassword, org.nspasteboard.TransientType, org.nspasteboard.ConcealedType and org.nspasteboard.AutoGeneratedType.
If you copy something from an app, and that app writes, say, a string to the pasteboard, and also specifies one of the data types above, the clipboard history will not pick it up.

If you have any suggestions, possible additions, questions or feedback regarding this, please do mail me.

I’ve also updated my privacy policy to clarify all of this.

Long story short: I’m not interested in anybody’s data. I don’t do any tracking, no usage statistics, and, if my apps use your internet connection, it’s exclusively for a specific feature that it offers to you, the user.

Take care : )
– Matthias

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