Maybe this is common knowledge and everybody knows this, but I’ve only recently come to learn how this works, so I thought I’d share.
I’ve been setting up Core Image filters like this for god knows how long:
The downsides are obvious: 1) I have to know the exact name of the filter (“CIColorPosterize”) 2) I have to know the keys and values it accepts (“inputLevels”)
Browsing the Core Image headers, I did find functions for these filters where you could just call let filter = CIFilter.colorPosterize()
Yet every time I tried, it never worked and would give me an error that CIFilter has no member 'colorPosterize'. Well, “Crap!“, I thought, this must be something that will become available eventually.
How wrong I was. This has been available for a while, you just need to know how to do it! The “trick” is to not only import CoreImage, but also (why!?) import CoreImage.CIFilterBuiltins:
And lo and behold, it works. I’m so happy, I could cry. I’m off to refactor some stuff now.
To start with: it’s difficult, but it can “work”. Sort of.
The star of the show, the MacBook Pro Mid-2012, running macOS Ventura (far right)
Why do this to yourself?
Quite frankly, I didn’t feel like shelling out ~€600 for a refurbished Mac Mini just to be able to test the odd app of mine with iCloud sync. It just seems overkill. And I did have this old MBP lying around, so I thought: why not give it a try? If all else failed, I’d still have the Mac Mini option, anyway. And a Virtual Machine doesn’t work, because you can’t log into iCloud / Apple ID in a VM environment for some reason. It could have been so easy…
I’d done it before, too. That 2012-MBP has served me so well over the years, with 6 partitions, reaching from macOS 10.10 Yosemite to macOS 10.15 Catalina, all for testing purposes.
It really speaks to the quality and longevity of these machines. Eleven years old, and still rocking.
Installing macOS 13 Ventura on the MBPMid2012
Getting macOS Ventura (released in 2022) to run on a Mac that’s not supposed to run even macOS 11 Big Sur (released in 2020) is surprisingly easy. Using OpenCore Legacy Patcher, it was pretty straight forward, with only one *gasp*-I-think-I-just-bricked-this-11-year-old-Mac moment that was resolved by a simple restart (a dialog appeared telling me the installation had to be aborted due to an unknown error, which had no effect at all). There’s really only one thing that I thought could be improved (complaining on a high level here, since it’s a miracle this works at all in the first place): You *have* to use an external drive to create the installer drive, you can’t use an internal partition. And you cannot use a partition of an external drive, you have to wipe that drive entirely. None of my USB thumb drives were big enough (the biggest I have is 8 GB), and all my other drives have more or less important data on them, so wiping one of them was out of the question. Thankfully, my girlfriend had a completely empty 2TB hard drive handy that she was supposed to use for backups (and never did), so I was able to use that. Once that was out of the way, it was all just a matter of following the on-screen instructions and waiting. And waiting. It takes a while.
Running Xcode 15 on it
It runs. It compiles. It fails to attach the debugger to the app (the LLDB RPC server crashes). But the compiled app continues running. So, debugging and coding directly on the Mac is impossible.
In hindsight, I think I may have overshot with macOS 13 Ventura and perhaps should have installed macOS 12 Monterey instead with Xcode 14, but I did what I did. It’s done. I’m not doing it over. You can’t make me.
In my case, it’s just as well. All I wanted to do was to test ScreenFloat 2’s iCloud sync with another Mac. And as far as that’s concerned, it’s working perfectly. It would have been the icing on the cake to be able to debug directly on that Mac, and commit changes back and forth, but what can you do… I debug on my main Mac, and then commit-and-pull the new code over on the old Mac, and then re-build. It’s tedious, but I can’t really complain. Well, I can. And I am. But I shouldn’t.
Other Observations
I’m making use of the Vision framework in ScreenFloat, and that completely crashes the app on the old Mac. Apparently, there is a reason why macOS Ventura is not supported on these old Macs.
I don’t think you could use this as a machine for work, as I’d imagine you’d run into a lot of little, annoying, and big, terrifying issues. For instance, I imagine Preview would crash a lot, since it also uses the Vision framework for its OCR and whatnot.
For testing apps in development, however, it might work, depending on what it is you’re testing/developing. I had to jump through some hoops (disabling my Vision framework code) to get my app to run. Your mileage may vary.
As a no-cost alternative to a refurbished Mac Mini at ~€600, I couldn’t be more happy. Here’s to my old, loyal MacBook Pro Mid-2012. It still has some life in it.
In System Preferences > Sound > Sound Effects, users have the choice to turn off user interface sound effects (as an example, that might be a “tick” sound when adjusting a slider, or a “lock/unlock” sound when clicking a Lock button).
But how do you respect that setting? NSSound surely doesn’t. Well, I found two ways to do it:
The “Easy” Way
macOS has a key in the standard UserDefaults object
com.apple.sound.uiaudio.enabled
If it is present and set to true, it’s safe to play the ui sound.
The “Safe” Way
What if the UserDefaults key isn’t present? Or you just want to be absolutely certain that your sound respects the user’s setting?
In an effort to support macOS Monterey’s new Shortcuts in Transloader‘s Link– and File Actions, I decided to run all the necessary CLI commands and AppleScripts in an external XPC service, mostly to separate privileges (running Apple Scripts (which aren’t precompiled and reside in the Application Scripts folder) in a sandboxed Mac app requires specific sandbox entitlements, which Transloader itself just doesn’t need).
I didn’t start with an XPC service, though. For testing and debugging purposes, I began with a new app in Xcode and ran all the Apple Scripts from there, which, after a bit of tweaking because I’m not “fluent” in Apple Script, worked just fine.
After subsequently moving them into an XPC service and thinking it would “just work”, I found that it didn’t. At least not reliably. Maybe every 8th or 9th time, yes, it did all it was asked to do, but every other time, it outright refused to execute Apple Scripts.
After two days of desperate attempts to get it working correctly, and almost giving up on the XPC privilege separation entirely for this, I figured I’d check to see if the Apple Scripts were being executed on the main thread. I did recall reading quite some time ago (I don’t know where, sorry) that Apple Scripts on background threads are iffy in their execution. Lo and behold, the methods weren’t run on the main thread. One quick dispatch_async to the main queue later, and everything worked as I expected it to.
So, the takeaway of all this, and the TLDR, is this post’s title: An XPC service’s methods aren’t necessarily run on the main thread. Go figure.
Addendum
If you’re wondering what my configuration for the XPC service is:
RunLoopType = NSRunLoop
ServiceType = Application
I did try dispatch_main as RunLoop as well, which worked too, but I just figured, for Apple Scripts, NSRunLoop would be required, but I don’t know for sure.