Software Development

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:

RunLoop = 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.

Read more

Yesterday, I was working on Transloader v3.0. In particular, on its push notifications and making sure everything syncs properly.
Today, I wanted to continue working on this, but found myself unable to. My Mac suddenly didn’t receive CloudKit push notifications anymore.
Even worse, neither of the delegate methods

- (void)application:(NSApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken

and

- (void)application:(NSApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error

weren’t called anymore – without any code changes. Something was wrong.

But here we are, a couple of hours later, and everything’s working again. Finally.
So, to save you some time should this happen to you, here’s what I did:

Restart

I restarted my Mac – the obvious solution. You might think. Only that it didn’t work, the problem persisted. But it’s still the first thing I’ll try if this returns.

Delete Containers, Group Containers, and CloudKit Caches

In Finder, delete

/Users/yourname/Library/Containers/yourAppContainerFolder/
/Users/yourname/Library/Group Containers/yourGroupID.yourAppGroupContainerFolder/
/Users/yourname/Library/Caches/CloudKit/yourAppID
/Users/yourname/Library/Caches/CloudKit/CloudKitMetadata* (*all files that begin with that filename)
/Users/yourname/Library/Caches/CloudKit/CloudKitOperationInfo* (*all files that begin with that filename)

Now launch Activity Monitor.app and force quit the following processes:

cfprefsd
apsd
notifyd
remoted

Now, I’m not sure you need to force-quit each and every one of those, but I wanted to relaunch any process that remotely looked like it had to do with remote/push notifications.

At this point, I restarted Transloader through Xcode, and lo and behold, it worked again.
On my MacBook Pro, where the same problem occurred simultaneously, I took the same steps. And it *didn’t* work. A restart was required, so your mileage – like mine – may vary.

Additional Info

There’s also this handy technical note from Apple, which can help debugging push notifications on both macOS and iOS.

Read more