California Dreamin’ – WWDC 2023

It’s about a week before WWDC 2023 kicks off in sunny California, so here’s a list of things I’m hoping to see come out of Cupertino. I’m skipping the AR/VR stuff, since it’s been speculated ad nauseam. I’m sure it’ll be cool and awesome and weird and probably not cheap. And maybe it’ll make me dizzy

TLDR; Better Xcode, Better Siri, Better Mac Catalyst

Xcode

Mostly, I just want Xcode to be better. Don’t crash. Don’t be slow. Is that too much to ask?

SPM Handling

We’ve all seen it. Switch branches, hit build and be faced with

Build operations are disabled: Package loading in progress. Please try again later.

I spend a lot of time working on Callisto, which is kind of a big app. We have dependencies split out into frameworks and it can take SPM a while to resolve all the packages when switching branches. So I hit this quite a lot. Usually, Xcode does a great job of queueing up actions when it’s busy. Like if you do a clean build and then run unit tests, it will finish the clean and then do the testing. That’s all I want for SPM resolution.

Better SwiftUI Tooling

I haven’t written a lot of SwiftUI. We toyed with it early on building Callisto, but it wasn’t ready for a big project like that. A couple months ago, we thought about trying to build a screen here or there with SwiftUI, but ran into roadblocks. One of the key features of SwiftUI is the live preview. To make those work, Xcode compiles bits of your code behind the scenes and shows them in preview pane. Callisto is a Catalyst app but has an AppKit plugin for doing Mac system things. Xcode could not handle that when trying to make a SwiftUI preview. Xcode would try to compile the AppKit code with UIKit and become very upset when it didn’t work. That left our foray into SwiftUI dead in the water.

Siri for Xcode

I’ve dabbled a bit with ChatGPT as a coding assistant. It’s great for small tasks with fiddly parameters. For instance, I needed to get the timestamp of some files. That’s the kind of thing that’s straightforward, but you don’t do it often, so you have to look up the specific API to stat the file and which attributes correspond to the creation date / modification date, etc. ChatGPT spit that code right out and I could move on to other things. But there’s friction there and the opportunity for a bespoke Xcode experience. I’m cautiously optimistic Apple will do something in this space, but I’m afraid it won’t be groundbreaking. Because, you know… Siri.

Catalyst and iOS Updates

Since Callisto is built with Mac Catalyst, these are the sorts of updates we’d love to see as developers.

Dynamic Type on Mac

Dynamic Type on iOS has been around for 10+ years. That’s the bit in iOS Settings that lets you make the text on your device a little bigger or a lot bigger than standard. All the apps that support Dynamic Type will pick up the change and text across the whole device changes size. That’s great! A boon for aging eyes everywhere.

But it isn’t supported at all on macOS. This recent announcement about new accessibility features coming in iOS 17 mentions

For users with low vision, Text Size is now easier to adjust across Mac apps such as Finder, Messages, Mail, Calendar, and Notes.

That sounds a lot like Dynamic Text on the Mac, so fingers crossed.

Auxiliary Window Replacement

Back in the day, lots of Mac apps used auxiliary windows that served as things like inspectors and floating tool panels. Apps like Photoshop had a bunch of these – paintbrushes, color panels, etc. You can still see these in AppKit apps like Preview and Quicktime Player, where the inspector is an auxiliary window. When you switch apps, it disappears to cut down on clutter. Right now, there’s nothing like that for Catalyst. The UIScene API for managing windows doesn’t have that level of distinction. Hopefully, at WWDC we’ll see a more mature Stage Manager implementation and at least some way of distinguishing main content windows and helper windows.

Multiple Processes on iOS

Callisto includes an embedded Python distribution for running the Jupyter Python Kernel. On the Mac, we just spawn a separate process and run Python normally. On iPad, we use a heavily modified Python to run in the same process space as the app to comply with App Store requirements. In practice, that hamstrings Callisto on the iPad a great deal. With a renewed interest in Pro apps on the iPad, it’d be incredible if we were allowed to run multiple processes and level the playing field between an M1 iPad and an M1 MacBook.

Yeah, never going to happen.

Other Stuff

Passwords

A lot of folks have called for a dedicated Passwords app instead of burying password management in Settings. I’m all for that, but I’d also love to see password sharing via your Apple Family. That’s really the last feature I’d need to leave 1Password behind. 1Password has been fantastic, but they’ve really pivoted to a corporate focus, so they’re less of good fit when I just want to share the Netflix password with my family. (Only for persons in my immediate family, residing in the same household. Netflix, if you’re reading this, I promise.)

HKSV Streaming API

I’m pretty heavily invested in the HomeKit ecosystem and I’m mostly happy with it. (I’m looking at you Siri.) I’ve got several third party apps that will stream video from my cameras, but they can only show live feeds. HKSV (HomeKit Secure Video) records events from these cameras and saves to iCloud, but scrolling back through time is only available via Apple’s Home app. Third party apps aren’t able to access the history of clips because there’s just no API for it. With HomeKit (hopefully) maturing a little more this year, Apple should make that available to developers.

Better Siri for Mac

Yes, macOS has Siri. But as underwhelming as Siri is on iOS, it’s worse on Mac.

In my notes, I wrote down “Not Brain Dead”. Siri just fails at the most basic things sometimes. When I try to open an app in my Applications folder with the phrase “Open AppName”, it fails maybe 2 out of 3 times. I’m not sure why it’s so bad and I don’t know of any way to debug it.

I usually use my laptop with the lid closed with external monitor, keyboard, webcam, etc. That means I can’t use “Hey Siri” for security reasons. I agree being able to turn off an always-on microphone is important, but if Siri is a serious feature, why can’t I explicitly grant permission for Siri to listen with an external microphone? It is interesting to note that “Hey Siri” does work with the ($1600) Mac Studio Display. That’s attributed to the onboard A13 chip.

Another bit of low hanging fruit for Siri is menu commands. At least it seems low hanging to me. If I’m using an app like Xcode, that has a menu command called ‘Build’, I would expect Siri to understand if I said “Hey Siri, Build”. But like the double meat burger, Siri is strictly off the menu.

But the real dream is a conversational Siri. We’ve all seen Iron Man. We’ve seen Tony talking to Jarvis like a person as he works. Could Siri ever do that in the context of Xcode? Playing with ChatGPT has teased this kind of reality. “Write a method to delete all the files in a given directory.” That’s a thing ChatGPT can do in a web browser window. Why can’t Siri to do that in Xcode? Imagine an Apple trained LLM that especially knew about Swift, UIKit, and all the Apple technologies. Add in the context of the project you’re currently working on. Talk about a developer accelerant. But that’s a big leap, so it’s doubtful we’ll see that this year, but maybe the first step?

Things We Won’t See at WWDC

Apple Silicon Mac Pro

Apple announced Apple Silicon for Mac at WWDC of 2020, three years ago, and the first commercial M1 Macs shipped in November of 2020. At the time, Apple estimated that the transition to Apple Silicon would take about two years. It’s been two and a half years since that first M1 MacBook Air shipped but the Mac Pro still sports an Intel chip. Something has clearly run amok.

But the rumor mill is pretty quiet on the Mac Pro front. Usually at this point, there would be at least some mention of the debut of a high profile, flagship Mac. It seems like we’ll be waiting until later in the year to see what kind of behemoth you can build out of lots of iPhone chips.

HomePod + AppleTV

Years ago, HomePod ran its own fork of iOS called AudioOS. Apple merged AudioOS with tvOS several revisions back so now both these home devices run tvOS. Because they do similar things – played media and power HomeKit – that makes a lot of sense. But the two devices remained separate at a hardware level – a smart speaker and a TV streamer. I’ve been waiting for the hybrid devices for years now. There’s the HomePod with a screen, like an Amazon Echo Show, that does HomePod things, but augmented with a display. And there’s the AppleTV with a mic and speaker. The mic let’s you talk to the TV without holding the button on the remote and the speaker acts as a center channel for multiple HomePod sound stage.

Those are both consumer products, so not the kind of thing they’d squeeze into the WWDC Keynote, especially with the AR/VR announcement, but maybe this fall, just in time for Christmas.

Will we see any of this at WWDC? I certainly hope so. But there’s only one way to find out.

No sleep ‘til DubDub!

Remember two door cars? Pepperidge Farm remembers.

Got some spam email today promising to use AI to increase my sales meetings by 10x! Thank you AI, you know just what I want. Coming soon – recreational root canals, weekend tax audits and more sauerkraut.

Pricing schemes for streaming services are weird. Most of them charge more for higher quality streams. So you get the same content, just an inferior version of it. It’s the equivalent of going to the Louvre and being allowed closer to the Mona Lisa if you paid more.

"There's something so human about taking something great, and ruining it a little so you can have more of it." -- Michael, The Good Place

About Final Cut for iPad… so many MacRumors commenters are writing it off because it’s a subscription - $5/mth or $50/yr. But Mac FCP is $300! So the part where Apple “wins” on this is at 6 yeas. If FCP is so good that you’re still using it in 6 years, then they totally deserve the money. It also means dabblers like me can pay $10 a year and do the two little hobby projects that come up where multi-cam would be handy. I can only imagine the fury if Apple had dropped a one-time $300 iPad app.

Better App Launching with SwiftUI for Unit Tests

TL;DR -- In SwiftUI, use a fake testing `App` instead of your real `App` 
to make sure you're actually testing your code. 

For unit testing, test coverage is an important metric. How much of your code base are you exercising during unit tests? Xcode is, unfortunately, not too bright when it comes to measuring coverage. It doesn’t have the intelligence to know if you’re “testing” a line of code, just that the line of code was executed. This is a problem right off the bat.

I’ve got a new app project in Xcode. The app does a little and I want to start adding tests before it gets too big. So I add a unit test target and Xcode plops in some empty tests. I run the unit tests, and they all pass since they’re empty. I check the coverage and it’s at 36%! How can that be, I didn’t test anything?

Xcode, the blissful idiot, is really reporting that 36% of the code was executed while running the unit tests. Usually, when an app starts up, it does some bootstrapping. You might set up your persistent storage, draw a couple of Views on screen, and maybe talk to the network. Xcode counts all that as “testing” because it ran during a unit test.

To make the test coverage more accurate, we need to do as little as possible outside of our actual unit tests. Jon Reid has a write-up of how to do this with a UIKit app by swapping out the app delegate during tests. But SwiftUI introduces a whole new startup sequence so we need a new approach for SwiftUI’s new app lifecycle.

After some futzing around, turns out it’s easy!

struct TestApp: App {					// 1
    var body: some Scene {
        WindowGroup {
            Text("I'm running tests!")
        }
    }
}

@main										// 2
struct TestDriver {
    static func main() {
        if NSClassFromString("XCTestCase") != nil {    // 3
            TestApp.main()
        } else {
            MyRealApp.main()
        }
    }
}

There are three key points that make this work:

  1. We need a dummy `App` struct to use instead of the real app. This simple stand-in circumvents all your usual app startup machinery. Instead of all the normal bootstrapping, we'll just get a window with the text "I'm running tests!".
  2. Remove the `@main` from your `App` implementation and add it here to `TestDriver`. Swift uses `@main` to figure out how to start your app. The `App` protocol provides a default implementation that, according to the docs, 'manages the launch process in a platform-appropriate way'. But by inserting our own wrapper layer here around, we can control _which_ `main()` is called.
  3. That brings us to the final point, use the good old `NSClassFromString` to decide if the testing bundle has been injected into our process. `XCTestCase` is only available during testing, so this is a reliable way to decide if unit testing is underway. Based on that, we can call the `main` method of either our real app or our testing stand-in. It turns out that the default implementation of `main` knows to use its parent struct to bootstrap the SwiftUI app.

Now when I run unit tests, my coverage is at 0.8%! That’s more like it. In order to boost my test coverage, I now have to actually test code. And the code coverage metric really starts to mean something.

Life, uh… finds a way

Kindle Unlimited porn discovered by parents; Apple ‘concerned’

Hey! No cameras! #BunnyButtFriday #RabbitsOfMastodon

Unit testing with UIDocumentPickerViewController – An Un-Googlable Bug

TLDR – If your unit tests crash with DocumentManager service tried to send a message to a deallocated host proxy, make sure you’re dismissing any presented instance of UIDocumentPickerViewController.

In our Callisto Xcode project, we’ve got a lot of unit tests. Like over a thousand. We’re at the point where if tests have a 0.1% chance of failing, then it happens every time. Our tests need to be really rock solid, or there’s no way we’ll get a clean run which is the only way CI will let a build through.

Some time ago, we started noticing the occasional test failure with an uncaught exception:

DocumentManager service tried to send a message to a deallocated host proxy

It would crop up when running test both locally and in CI. With so many tests, we get the weird edge case now and then, but they’re not worth the time to track down. We ignored it. With the recent update to Xcode 14.3 and macOS 13.3, the DocManager exception went from occasional annoyance to ‘omg, this happens every time I unit test on iOS’. So now we have to fix it.

But what’s a DocManager? IDK – there’s nothing with that name in our code and nothing in the docs about an Apple framework called DocManager. Looking through the traceback, it’s pretty obvious that it’s some kind of internal Apple thing. Surprisingly, a Google search for this error returns absolutely no results. That’s never a good sign.

But what’s a DocManager do? Callisto is UIDocument based, so maybe DocManager manages documents? A bunch of the unit tests open instances of a UIDocument and aren’t 100% about cleaning those up, so maybe some housekeeping will help. Cue the plumbing montage. We added a some bookkeeping to make sure that any open UIDocuments were closed at the end of each test, so now we’re sure there are no dangling UIDocuments. No impact – still the DocManager throws the exception.

This particular problem is a huge pain to track down. Somewhere in the tests, things get into a bad state. Later, while another test is running, some background thread discovers the bad state and throws an exception. The cause of the crash and the actual crashing are quite loosely coupled, making it hard to pin down the culprit. It also means that the offending tests will run just fine by itself, but only cause a crash when a large number of tests run, giving the background issue time to percolate to the surface.

After a couple hours of testing the tests and narrowing down which ones fail, it started to look like UIDocumentPickerViewController might be involved. We’ve got some ViewControllers that open a docPicker and get feedback via the docPicker’s delegate methods. To test those, we programmatically tap a button to cause the dockPicker to be presented, get a handle to that docPicker and call the delegate with the docPicker and some fake results. This works great for testing! Except for that pesky exception that gets thrown now and then.

If we take a closer look at the exception, we’ll see there’s a little more info attached in the userInfo dict, specifically a file and line number, DOCRemoteViewControlller.m line 42. Not that we have access to the source, but the name of the file, “Remote View Controller”, does offer a hint of its purpose. Those docPicker view controllers offer our app an escape hatch out of the sandbox and into the rest of the file system on the device. In my limited understanding, the docPickers interface with a separate process that manages file system access, so they actually represent some state from another (remote) long lived process. In the end, these dangling, un-dismissed docPickers were the root of the problem. Make sure all your UIDocumentPickerViewControllers are dismissed properly and the problem goes away.

Whew!

Excuse me waiter, there’s a bit of hay in my water. #RabbitsOfMastodon

A big ruby eyed white rabbit looks at the camera, expressing her impatience because there is a large hay cake in her water bowl.

Philosophical Dragon

MarsEdit 5

Tried to pay for an upgrade to MarsEdit 5 this morning, but it wouldn’t let me. The web store said I had purchased MarsEdit 4 too recently, and it gave me a free upgraded to version 5. Nice!

Greg Garcia’s Sprung on Amazon Prime Video is quite good. He’s the creator behind My Name is Earl and Raising Hope. If you liked those, Sprung will be right up your alley. Some familiar faces too. So good.

The Thinking Brick

A New Watch!

I took the plunge and ordered a new Ultra Apple Watch sight unseen. I’m very excited for a new wrist computer, especially with a big orange button! Seems weird though that there’s no titanium watch band to dress it up. I guess Apple is going full tilt on the outdoor adventure lifestyle to start. The fancy matching titanium band will probably show up in the spring.

(I desperately want to make an Ultraman joke, but obviously I’m no Ultraman.)

Holy Shift! Koenigsegg’s New Transmission Is a 6-Speed Manual and a 9-Speed Automatic

I always wondered if someone would build a clutch-by-wire system like this where there’s a gear shift and a clutch, but not physically linked to the drivetrain. This Koenigsegg gearbox sounds like just the thing. The problem now is that we’re all switching to gear-less electric motors, so will this come to electrics too? Make ‘em feel like a Miata? I guess Dodge is going to try with the eRupt.

Holy Shift! Koenigsegg’s New Transmission Is a 6-Speed Manual *and* a 9-Speed Automatic:

"We still are in the process of developing it, but it's already crazy good. When we are done with it, I don't think anyone will be able to tell it apart from a traditional manual. That's the objective. It should feel like a mix between a Mazda Miata and a Ferrari gated shifter. The best of the two worlds."

Serendipity

On the occasion of a blind squirrel finding a nut.

Wordle 426 2/6
⬜🟩⬜🟨⬜
🟩🟩🟩🟩🟩

Sponge worthy?

My favorite new unix too is definitely sponge. It’s from the moreutils suite, but the easiest way to get it on your Mac is a quick brew install sponge.

You know how sed can edit a file in place? Dope, right? sponge makes it easy to do that with all the regular unix tools. If I want to fix a DOS text file by removing the carriage return, I always try doing

cat my_file.txt | tr -d "\r" > my_file.txt

But that doesn’t work because it starts writing to the file before it’s all read and a lot of times, the file ends up empty. sponge to the rescue!

cat my_file.txt | tr -d "\r" | sponge my_file.txt

sponge will read everything from standard in and then write it out to the given file, so this works like you want it to, even for big files.

Also great with jq for prettifying JSON files in place too.

Three Habits

The three hardest habits to break are heroin, carbs and a steady paycheck.

– Nassim Nicholas Taleb (paraphrased)

Illinois Tech ‘spinout’ startup Influit Energ | EurekAlert!

A research group out of Chicago has developed “liquid batteries”. I have no idea how the chemistry of this works, but it sounds like you could refuel an electric car a lot like you’d gas up an ICE car, and in a similar amount of time. And about a billion other applications.

Illinois Tech ‘spinout’ startup Influit Energ | EurekAlert!:

The unique high-energy density liquid format of the NEF flow batteries allows use of the same fluids in different devices, meaning fluid, charged at the recharging station from renewable energy sources or a grid, can be used to rapidly refuel vehicles, or for stationary storage and other large portable applications,” Timofeeva says. “Discharged fluid can be returned to a recharge/refuel station for recharging or be charged inside the device by plugging into the power source.

Muppet Songs: Ernie - Rubber Duckie - YouTube

Today’s earworm…

Muppet Songs: Ernie - Rubber Duckie - YouTube:

Daring Fireball: ‘What’s the Deal With Water Bottles?’

From the New Yorker via DF, a delightful survey of comics and their water bottles. As a kid with cable, I watched a lot of standup, so I found this extremely interesting. And the presentation is molto bene – the animated magazine version of the web.

Daring Fireball: 'What's the Deal With Water Bottles?':

Tricorders

When I was a kid, I expected my tricorder would look like this

Yokogawa AQ1200

But it turned out like this

158438 phones review iphone 13 pro review a lesson in refinement image5 ws2nobt8ak

Which still seems weird.

Via WIL WHEATON dot TUMBLR dot COM - vizreef: Yokogawa // AQ1200 Multi Field...:

Two’s company. Get lost nerd.

The MYSTERIOUS Error.

Callisto is a Catalyst app, mostly running iOS style code on a Mac. But there are some things that Catalyst apps can’t do – like run a python server as another process. But Mac apps can!

There are a lot of instructions around the Internet about how to use AppKit code within your Catalyst app to do those Mac things that Catalyst doesn’t cover yet. This article from Steve Troughton-Smith was our guide.

We also want to do auto-updates like a regular Mac app. We’re distributing outside the Mac App Store for Reasons, so we’re in charge of our own updates. Yep, dear reader, that means Sparkle, which should be fine, right? So we get the Sparkle framework and link it in to our AppKit bundle which is a plugin for our main app. Compile, run and 💥. This error is staring at us from the console.

code signature in (<full path redacted>/Sparkle.framework/Versions/A/Sparkle) not valid for use 
in process using Library Validation: not allowing mapping of development code into production process

What does that mean? After some furious googling, we’re left empty handed and confused. The worst kind of error is that special, new error that no one else has ever seen. Bollocks.

After some futile futzing around in the dark, I give up and burn a ‘code level’ support ticket with Apple. I send a fairly detailed outline of the problem and the next morning I get back a nice response asking for more details. In particular, how are the app and the framework code signed? (You can check that with codesign -vvvd <.app or .framework>.)

And immediately, I see it.

App Signature:

Authority=Developer ID Application: Oak City Labs, LLC (XXXXXXXXX)

Framework Signature:

Authority=Apple Development: Continuous Integration (YYYYYYYYY)

The app is signed with a production identity while the framework is signed with a development identity. That’s what that weird error meant about not allowing mapping of development code into production process. The codesign docs point out how frameworks and apps need to both be signed with the same Team ID (which I checked), but don’t mention this distinction between production / development signing identities.

I update Xcode so that the AppKit bundle was being signed with the same production identity and all is well! We are fully Sparkle enabled! Curiously, the AppKit bundle has always been signed with the development identity before, yet it loaded fine at runtime. That must be one of those fine distinctions between a bundle and a framework that I don’t quite understand.

But now we celebrate! 🎉🎉🎉