A few weeks ago, my iPhone started having a serious problem: it could no longer search for things. This bug affected the App Library, Mail, Notes, Messages, Settings, and more. Having a device that couldn’t find things was crippling.
All signs pointed to this being an issue with the Spotlight index on the device: the database was corrupted and unable to do queries or updates.
Of course I searched for clues on how to fix this issue and tried the following things:
spotlightd for errorsNone of these things worked.
All of the hints I saw online ended with “if this doesn’t work, just Reset All Settings”. When you go to do that, you’re presented with the following dialog:
I’ve been adjusting my settings on my phone since 2007 and honestly couldn’t imagine the fallout from resetting them to defaults. All I knew is that this would be incredibly disruptive to my life for several months as I stumbled over things that needed setup.
For example, would a nice evening in a restaurant with dim lighting be interrupted when I triple-clicked the side button to get the Magnifier to read the menu? Being taken out of the moment by tapping around in Settings was not something I wanted to risk.
That left me with one alternative: a full device backup and restore. I did this on my Mac to make it as speedy as possible, but the process still took a couple of hours. During all of which I was without my primary device.
Afterwards, there were immediately problems:
In short, this took several days to sort out. And the entire time, it pissed me off because it was entirely avoidable.
On the Mac, there is a simple procedure to rebuild the Spotlight index. This same affordance is not available on iOS.
It just works, my ass.
Spotlight is a database that’s accessed by a lot of processes in a lot of different situations. It’s reasonable to expect that all this activity can uncover bugs that corrupt the index. The kinds of issues that are hard to reproduce, but easy to repair with a simple button labeled “Rebuild”.
Why doesn’t that button exist on iOS? Maybe it’s a product manager in denial. Maybe it’s because the Spotlight developers are always working on fresh database and don’t ever see the issue (my database had likely existed for months or even years).
Whatever the reason, there are plenty of YouTube videos and Apple Support discussions that show this issue is widespread. My proposal is to add a button to aid customers:
Until this happens, Spotlight just isn’t right.
Apple folks: FB22651086
One of the things I love doing is naming things. Whether it’s for products, or things that I encounter daily, it’s a habit.
Fifteen years ago, I started a list for our new dog, Pico. My wife and other family members joined in on the fun. Last December, we updated the note for the last time.
I’ve shared this list over the years and everyone gets a kick out of it, but the main reason I’m putting it up here is so I have a place to remember all the good times we had together.
The names are in chronological order, the photos are not.
Our first day together.Snooty
Pico-bahn
Snoot Butt
Bud
Always on the lookout for suspicious activity.Officer Pico Rivera, Dog Cop
Snootsicle Von Tootsicle
Struttin’ Von Buttin
Snootsie Von Tootsie
With his 10× doppelgänger at the dog park.Mr. Toothpick McShivers
Piconator
Snoots
Squeeko
Snootsicle Bootscicle
Packed wet sand is the perfect medium to show off your skills.Sandy Dandy
Sandicle Dandicle
Budly Studly
Always pay attention to nearby vehicles. Or take a nap.Captain Snoot of the S.S.Picobahn
Barkey McLoudbutt
Power Snoots
Lord Voratio
Rooter
Barky Boy
Sir Barksalot
High Falutin’ Snootin’
The Force was definitely strong with this one.Obiwan Snootobi
Turd Burgler
Snoot Doggy Dog
Sheik Yer Snooty
Sir Prancelot
Pink Butt
No shortage of freckles or desire to show them off.Freckle Pecker – Deck Commander
Snooticus Tooticus
Bupster Pupster
Snootrition
Nudie Snoodie
Food Genius
Quicker Licker
Low tide is the best tide.Moto Pico
Pink Gooba
A dog can only take so much hot and dusty trail, so hitch a ride.Bud Bud
Bumpy Gut
Platemaster Supreme
Mister Bits
There was no limit to his licking abilities.Dr. Bits, Molecular Foodologist
The Leaf Thief
Hellen Killer
Mister Biscuits
Snootrous Boutros-Ghali
All kinds of sizzle.Little Sizzle (Rapper Name)
Lumpystiltskin
A grunt is the best way to express your unhappiness with a situation.Grunty McRunty
Yawny (pronounced Yanni)
Mister Slurps
Hacky Jack
Frosty the Dogman
The pleasures of old age and good friends.Frosty Face (from Lori)
Bubbie
Frankenbutt (after mast tumor removal)
Zipper/Zippy Butt
Like shadows, we’re not really here, but the memories always will be.
Our trusty Brother MFC laser printer and scanner was getting long in the tooth and my wife wanted color printing. After a lot of research, she landed on the Canon MF644Cdw and placed an order.
Initially, we were both happy with the new printer. The print quality is great, the hardware is solid, and the touchpad for configuration and management is better than most.
But after a few months it became apparent that the printer’s networking is completely screwed up. The root of the problem is with Canon’s firmware: it’s not compatible with Apple’s Bonjour.
So how widespread is this issue?
Here is a six year old support thread about “offline” printer on the Mac. And if you search Google for “Canon offline Mac”, there are a ton of web pages and videos explaining how to “fix” the problem.
The problem with the fix is that it’s only temporary. Based on my personal experience, you need to re-fix things when you’re in a hurry to print a shipping label or report. Adding insult to injury, everyone that’s sharing the printer, whether it’s a family member or a work colleague, will need to reset their printer configuration, too.
Initially, this is a mild inconvenience. But after doing it hundreds of times, I renamed our printer “Canon MF” – the initials do not mean Multi-Function.
If you are reading this and still within the return window for the printer, head to your car or web browser right now. As you’ll see below, this is not a printer that will be easy to deal with on a Mac. It will be a purchase you regret.
If you can’t return the printer, keep reading and I’ll explain how things are broken and present a workaround that will prevent the constant “Offline” situation.
If you just want a permanent fix and don’t care about the details, scroll down to You Can Fix This.
Digging InSince I could find no reports of the weird “Offline” behavior on Windows, I began by investigating the technologies that are specific to the Mac and other Unix-based systems:
CUPS felt like an unlikely culprit: everything prints great when it’s working. After checking the printer drivers against the CUPS documentation, this intuition proved correct.
But when I started looking at Bonjour, it was another story.
The following Terminal command can be used to see what printers are on your local network:
$ dns-sd -B \_ipps.\_tcp
The command is cryptic, like all things in the Terminal, but it means “do DNS Service Discovery, browse for Internet Printing Protocol over HTTPS”.
NOTE: If using a Terminal is new to you, don’t be afraid to try the commands I’m showing: none of them will harm your Mac. You will find the app in the Applications > Utilities folder. You can copy all the text after the “$” and paste it directly into the app. Closing the window will terminate the command.
The command output will contain multiple columns, but it’s the first two columns with a timestamp and add/remove status, that show what’s wrong:
$ dns-sd -B \_ipps.\_tcpBrowsing for \_ipps.\_tcpDATE: ---Sun 01 Mar 2026---14:31:17.408 ...STARTING...Timestamp A/R Instance Name14:31:18.740 Add Canon...(28)14:33:21.446 Rmv Canon...(28)14:33:25.577 Add Canon...(28)14:35:34.505 Rmv Canon...(28)14:35:34.952 Add Canon...(28)14:37:33.932 Rmv Canon...(28)14:37:35.251 Add Canon...(28)14:39:37.001 Rmv Canon...(28)14:39:43.664 Add Canon...(28)14:41:58.121 Rmv Canon...(28)14:42:09.361 Add Canon...(28)14:50:13.326 Rmv Canon...(28)14:51:41.923 Add Canon...(28)14:53:39.560 Rmv Canon...(28)14:53:45.726 Add Canon...(28)
Typically a printer will added when you start the command, and removed when your Mac goes to sleep.
The Canon printer removes itself every few minutes. To the rest of the network that’s using Bonjour (including everyone’s printer settings), it looks like the printer was powered off and on again. Hence the “Offline” state and the red dot in Printers & Scanners settings.
The “Offline” state is also likely to happen when you wake your Mac. The add/remove nonsense is caused by the printer and when your Mac is asleep it may miss the state change. When this happens, it can take 10-15 minutes for the red dot to turn green. When you are in this situation, you can’t add a new printer because it doesn’t exist in Bonjour. You just have to wait.
This is the root of the problem. The constant change of state eventually corrupts the cache used by Bonjour. This, in turn, breaks the CUPS configuration and your printer is either temporarily or permanently “Offline”.
A fancier version of this command speaks when the printer adds or removes itself: this lets you do things like look at the status of the printer in System Settings. After you hear “remove” you’ll see a red dot and “Offline”. An “add” turns the indicator green and shows “Idle”. This will be annoying after awhile, but it does give you great feedback about how Bonjour is messed up:
$ dns-sd -B \_ipps.\_tcp | while IFS= read -r line; do echo "$line"; echo "$line" > /tmp/line.out; if grep -q "Rmv" /tmp/line.out; then say "Remove"; elif grep -q "Add" /tmp/line.out; then say "Add"; fi ; done
What’s in a Name?When I started writing this report, the Bonjour name for our printer was “Canon MF642C/643C/644C (a5:d7:ad) (16) (a5:d7:a (a5:d7:ad) (28)”. A few days later it changed to “Canon MF642C/643C/644C (a5:d7:ad) (16) (a5:d7:a (a5:d7:ad) (10)”. I have no idea what caused this change, and both the names are ridiculous.
The only thing that’s important in this change are the number in parentheses at the end. Going from “Canon…(28)” to “Canon…(10)” breaks the printer configuration of every Mac on your network.
You can see this by checking the CUPS configuration:
$ lpstat -vdevice for Canon\_MF642C\_643C\_644C: dnssd://Canon…(28).\_ipps.\_tcp.local./?uuid=GUID
There is no longer a device for dnssd://Canon…(28)._ipps._tcp. So when CUPS periodically checks the print queues and it can’t find the name, the printer is permanently offline.
When a permanent “Offline” happens, you will need to update your Mac and iOS devices:
So What’s Really in a Name?So what’s the deal with the “Canon…(28)” that eventually turned to “(10)” and broke everything? These numbers are appended automatically when another device is already using the same name. If “Craig’s Mac” already exists on the local network, if you try to add it again, Bonjour will use “Craig’s Mac (1)”. Look familiar?
It turns out this Bonjour cache corruption happened on macOS about 11 years ago and it was a clusterfuck. Some devices on your local network, such as Apple TVs or HomePods, act as a backup system that can provide DNS information when other devices are asleep. When this cache gets corrupted, all hell breaks loose.
A printer that constantly adding and removing itself will eventually poison the cache and the next number in sequence will be used. And every time the corruption occurs, new stuff gets added to the end and you eventually end up with “Canon MF642C/643C/644C (a5:d7:ad) (16) (a5:d7:a (a5:d7:ad) (10)”. Lovely.
Can Canon Fix This?I have no idea what’s going on inside the printer, and from all the support requests on their site, neither does Canon. All I can see is that printer needs to quit and restart Bonjour every few minutes.
Typically removing a DNS service instance happens when the app or process that’s handling the requests needs to quit. It should happen very infrequently. Most likely because you’ve turned the printer off.
Instead of acknowledging issues with Bonjour, the support folks talk about changing your printer configuration. And conveniently ignore the folks who note that they’ve done this “hundreds of times”.
This is not a problem that can be fixed by a new downloadable driver or by power-cycling the printer. It is a problem that will require new printer firmware from Canon. The last firmware update was 2 years ago.
It’s a huge pain in the ass and based on my personal experience it happens when you need the printer right now. Adding insult to injury, everyone that’s sharing the printer, whether it’s a family member or a work colleague, will need to reset their printer configuration.
So I set out to find a way to avoid Canon’s shitty Bonjour implementation in our printer set up.
You Can Fix ThisFor the most part, it’s possible to use the Canon printer without its “cycling” behavior in Bonjour. The printer will still be generating and changing gobbledygook names, but we will ignore them.
The trick is to use a static IP address. Since Bonjour’s main task is to find your printer’s IP address using a device name, we can skip that step if we already know where the printer is on the local network.
VideoGuy discovered this workaround and shared it on the Canon forums. I’ve made some simple tweaks that let your Mac know about the full capabilities of the printer (duplex pages, color support, supply levels, even a non-generic icon).
Say that you see “Start: 192.168.0.20 Stop: 192.168.0.150”. Picking a static IP of “192.168.0.180” is a safe choice because its past “192.168.0.150”.
You can double-check that this address isn’t in use with this Terminal command:
$ ping -c 5 192.168.0.180PING 192.168.0.180 (192.168.0.180): 56 data bytesRequest timeout for icmp\_seq 0Request timeout for icmp\_seq 1Request timeout for icmp\_seq 2Request timeout for icmp\_seq 3--- 192.168.0.180 ping statistics ---5 packets transmitted, 0 packets received, 100.0% packet loss
The “Request timeouts” and “100% packet loss” indicate that there isn’t a device at this IP address and will be safe to use on the Canon printer.
Next, drill down using the following path:
Menu > Preferences > Network > TCP/IP Settings > IPv4 Settings > IP Address Settings
By default, the printer acquires its IP addresses automatically and “Auto Acquire” is turned on. The “Manually Acquire” setting is disabled.
Turn “Auto Acquire” off, and “Manually Acquire” will get enabled. Select it and you’ll be presented with a screen where you input:
IP address: 192.168.0.180 Subnet mask: 255.255.255.0 Gateway address: 192.168.0.1
The first field is the one you want to focus on: it should be set to the static IP address that you picked above.
The last two fields are probably already set correctly because the values were retrieved during the “auto acquire” – if you have problems with them, refer to your router’s settings.
After applying the settings, restart the printer, and run a quick test of the IP address with:
$ ping -c 5 192.168.0.180
Instead of timeout errors, you should see “0.0% packet loss” and know that you have a good connection to the printer. If you see errors, go back to printer’s network preferences and double-check your work.
On your Mac, go into Printers & Scanners settings, and select “Add Printer, Scanner, or Fax…”.
Instead of using first panel which shows all the Bonjour devices, pick the middle icon of the Add Printer dialog: this panel will let you add your printer using the static IP address you created.
Enter your static IP address into the first field, then select the “AirPrint” protocol. Make sure that the driver is “CNMF642C/643C/644C” (or whatever is appropriate for your hardware).
Name the printer whatever you want: I chose “Canon MF” (again, not because it’s “Multi-Function”).
Click Add and you will now have a new printer that never goes “Offline”. Woo-hoo!
Note that this workaround only affects printing — when you need to use the scanning features, you will still need to setup a device that uses Bonjour. This configuration will break just as often as the one for printing, but in my experience it’s not so disruptive. I typically use the scanner when doing taxes or other financial things, and having to make a new configuration isn’t a big deal because I’m not in a hurry.
ConclusionThis issue has taken countless hours to resolve. It’s a bug that appears and disappears randomly and is completely confusing from an end user’s point-of-view.
As Mac users, we’re used to things “just working” and in this case that’s just not the case. Bonjour makes finding devices and services on your network “just work”. That is, until a Canon printer gets involved.
If anyone at Canon is reading this, please get in contact: I would love to test any firmware fixes for these Bonjour problems. I’ve been building Mac apps for over 40 years and have gotten pretty good at testing stuff.
A Christmas ornament from my sister-in-law that sums up my year.January started as normal as can be expected when malicious grifters start making basic decency a radical idea. It turns out the anxiety associated with these political events would be the least of my problems throughout the year.
It felt great to finish up a 12 month project and release the first version of Tapestry. I celebrated with a trip to Louisiana visiting my wife’s birthplace, exploring islands and bayous, and eating more seafood than I thought possible.
An impressive spine.In April, I turned 65 and signed up for Medicare. I was about to learn how important this was.
Towards the end of that month, I started feeling some tingling in my left index finger and some pain in my neck, especially after working at the computer all day. Initially, I chalked it up to the normal aches and pains of growing older, but the pain just wouldn’t go away.
The next month was marked by tragedy. On May 17th, while taking our dogs for a walk before dinner, our girl Jolie was attacked by dogs that had escaped from their yard. It took every ounce of my strength to get two 50 pound dogs without collars off of our 15 pound pup, but I rescued her, did some quick triage for her open wounds, and rushed her to the vet for four hours of surgery. We were both wrecks, but made it to see another day.
We’re both getting too old for this shit.Jolie started to recover from her injuries, but she was a 15 year old with a weak heart. On June 4th, I found her unconscious outside the door of my office. She died peacefully and the loss was added to the year’s pain tally.
I also had adverse effects from the dog fight: the pain in my neck had gotten much worse. The adrenaline rush made me move my neck and arm in ways that turned an irritating pain into a persistent one.
In July, we travelled to San Diego to see an outdoor concert. I was living with neck pain all day, every day, and when I couldn’t lift my head to watch the show, I knew I needed help. On the 16th, I had my first appointment with a local chiropractor. X-rays showed degenerative spine disease, which is consistent for someone my age: pain being caused by old cervical vertebrae and pinched discs.
I was staying active in spite of the pain in my arm and neck. My swimming stroke sucked thanks to my limited arm movement and neck pain limited the length of my bike rides.
On August 3rd, while riding my e-bike to Trader Joe’s to do some grocery shopping, I was hit by a car. Someone blocking the road at a 90 degree angle decided to backup while only looking at the camera on their dashboard. They didn’t see me riding in the rightmost lane of traffic.
I ended hitting the D pillar of a SUV with my left shoulder and tearing my AC joint. Then I was thrown from my bike and landed hard on asphalt. The impact broke five ribs and I immediately had a new source of pain on my left side.
The paramedics arrived and got me to the closest emergency room. That’s when we all discovered I had another problem: a punctured lung that was causing my chest cavity to fill with air. This presented itself while lying down waiting for a CT: it’s impossible to express the panic of not being able to breathe or talk. Luckily, my wife was in the room and screamed for help that resulted in a temporary chest vent while I was rushed to a trauma center. Another ride with the paramedics, this time with lights, sirens, and lot more speed.
There was a team waiting for me, and I got a dose of ketamine, followed by a chest tube that was inserted while I was (barely) conscious. As the surgery was ending, the head nurse asked me how I was feeling, and my response was “I’M TRIPPING BALLS”, which got a laugh from everyone in the operating room. It also helped me understand a billionaire that needs the substance to feel joy in his life.
I spent a total of three days in the hospital as the doctors monitored my chest fluids. My main source of pain at that point was the broken ribs: sneezing, coughing, or laughing hurt like hell. What didn’t hurt was my neck and arm: as one nurse joked when I was telling them about my situation: “Hey, you got a free adjustment!”
The view from my hospital room and it’s missing a K.I felt good enough to spend some time working on Tot 2: all of the App Store purchasing code was done while in a hospital bed. It was a nice distraction and helped us ship the update at the end of August.
Soon after the release I read a blog post that rang true: Irrational Dedication. Both of the Iconfactory’s major releases during the year were willed into existence. Tapestry after a year of work for a new product category (“timeline apps”) that was difficult to explain. Tot while working through various stages of pain.
It took about six weeks for my ribs to heal completely. While that was happening, September presented another health issue to deal with: this time for our boy dog, Pico. What started as a small bump on his butt quickly grew into a large Mastocytoma (Mast Cell Tumor). At the end of August he had surgery to remove the mass and he got a new nickname: “Zipper Butt”.
“Daddy, you should try this look.”
Click or tap to view.We were about to put a twist on the old adage about dogs looking like their owners: this owner was about to look like his dog.
This was also the time where my original neck pain returned. It turns out the brain can’t handle more than one pain input at a time – the broken ribs put the nerve pain on the back burner. Chiropractic treatment was providing only temporary relief, so I tried acupuncture in October.
Then, in November, all hell broke loose. At the beginning of the month we took a car trip to Tucson for a family event. I spent most of the trip through the desert with shooting pains through my arms: agony for hours on end.
A week or so later, I started noticing problems with my ability to walk and a numbness throughout my torso. The nerve pain felt like the onset of paralysis. Shit was getting serious.
My primary care physician prescribed muscle relaxers which had no effect. My chiropractor scheduled an MRI on the 14th and we got the results on the 17th.
The MRI showed that I had a mass in my spine that was pressing on the fluid that protects and nourishes the spinal cord. My neck was screwed up more than anyone expected and needed immediate attention. A referral to oncology at Hoag Hospital got us into the ER on the 19th.
MRI showing the white spinal fluid around the dark spinal cord being invaded by a mass of gray tissue.
Click or tap to view.There was just one problem: my goddaughter was getting married on the 19th. On a sandy beach, at the end of a rocky path. And I could barely walk.
I’ve been a part of her life since birth and not being able to share this important moment broke me completely. I spent most of the 18th sobbing and feeling shitty about the cards that life had dealt me.
The tests included a two hour full–body scan in a noisy and cramped MRI. Plenty of time to contemplate life and realize that the last time I had been at this hospital was when my goddaughter was born 36 years earlier: a day spent translating for two women who were about to be grandmothers for the first time and didn’t speak each other’s language. (Little known fact: I’m an Italian godfather.)
All the tests confirmed the spinal mass and provided a plan for treatment. I was given steroids to reduce inflammation and felt immediate relief: it was the first time I had been without neck pain in about eight months. Next, a cervical laminectomy would remove part of my spine and permanently relieve the pressure on the spinal cord that was the source of my pain. It would also allow the doctors to obtain a sample for pathology: to determine if the mass inside my spine was benign or malignant.
Twenty-nine staples later and my neck felt a lot better.
Click or tap to view.The operation was a success and I was home in time for Thanksgiving. I was so thankful for my wife, family, friends, and medical professionals that were helping me through this rough time. And for the end of a week with opioid constipation.
After the holidays, it was not a shock to learn that the mass was malignant. Everything we had seen suggested that the source was lymphatic. Additional tests, including a PET scan and a lumbar puncture (a.k.a. spinal tap), made it clear that I have a follicular lymphoma in both my blood stream and spinal fluid.
The good news is that this is not a particularly aggressive variant and has therapies that have been effective for decades. It’s going to be something that takes months to treat and will require some hospitalization. But the doctors and I are both optimistic about the outcome.
The surgery to relieve neck pain continues to heal: I still have a bit of muscle soreness but the persistent pain is completely gone. Another reason to be hopeful for recovery.
I still have the nerve damage that caused my initial paralysis. The hope is that as the spinal mass shrinks, my walking and numbness will improve. And the only way to make that happen is with both physical therapy and chemotherapy, both of which I started on Christmas week. Happy holidays!
Luckily, I didn’t have any major issues during the first infusion, but a week later I’m still feeling the effects: overall fatigue, a queasy stomach, and a weird taste in my mouth. Dietary restrictions like giving up red meat, fried foods, and processed sugars seemed important a week ago. Now, the medicinal marijuana my nephew got me for Christmas feels much more significant.
It’s clear there is a long road ahead of me, and while I may have less spine, I am not spineless. The irrational dedication I mentioned earlier is now focused on getting myself back to health.
My personal goal is to swim to a buoy in the Pacific Ocean. It’s going to take a lot of effort to make that happen and I know that stating your objectives is the best way to meet them. (One of the reasons for this blog post, in fact.)
My goddaughters heard about my aspirations and handmade an inspirational gift for Christmas: candles of the buoy itself and the kelp and Garibaldi underneath. I’m going to burn it all down.
Burn, baby, burn.I had originally wanted to end this essay on that positive note, but the year had other plans. The week after Christmas, Pico started showing signs of abdominal pain and inappetence. He had developed a mass on his liver and spleen, and given his age, the prognosis for recovery wasn’t good. I always knew that saying goodbye to my constant companion of the past 15 years was not going to be easy, but never imagined doing it with all this other shit going on in my life. Consider my ass well and truly kicked.
Even if I’m getting out of the year on emotional fumes, I lived to see another one. My little boy won’t be there to dance around excitedly as I get out of the water this summer, but he will always be a reminder that I never give up.
No, we’re not doing science at California’s most beautiful lake.
We’re looking for bugs.
A popular cross-platform app development framework called Electron is using private and undocumented API that’s causing system-wide slowdowns in macOS Tahoe.
We’re hearing from customers that some of our apps are running slowly on Tahoe and I suspect that this bug has something to do with it. Unfortunately, it’s hard for customers to check which version of Electron is being used and see if that might be a cause. So I decided to do something about that…
Luckily there’s a script written by Tomas Kafka that lets you check all your apps quickly and easily. I took that script, updated some parts that required Xcode to be installed, and wrapped it up in an AppleScript applet that’s easy to download and run:
Download TahoeElectronDetector.zip
When you run the app, you’ll see a short introduction:
The first time you run the app, you’ll see a warning that the app was prevented from modifying other apps on your system. This is “normal” because the app needs to read other apps to do its job:
After all apps are checked, you’ll see the results:
Eventually, you’ll see ✅ in that window and know that one or all of your Electron apps have been updated.
If you’re one of those people who’s wondering when it’s a good time to upgrade to Tahoe, you can run TahoeElectronDetector on older versions of macOS and give yourself an idea of when it’s safe to move to the new operating system.
Additionally, there’s a website that lists the status of the most popular apps. This will be helpful in locating newer versions since some of them will not update automatically.
If you’re a Mac developer who’s hearing from customers about weird slowness, feel free to point them at this web page or give them a copy of the app to check their own system. If you need the source code, it can be downloaded here.
And if you’re a developer, this is your periodic reminder not to use private and undocumented parts of an API. They will break, and in cases like this, it will be spectacular.
In a previous essay, I briefly expressed some thoughts about why Liquid Glass is inappropriate for the Mac:
I’m having a much harder time seeing how Liquid Glass will benefit other platforms like the Mac or Apple TV (where Apple doesn’t even make the screen). Forcing tactility where it’s not needed or wanted feels like a misstep.
I’ll now go into depth regarding these thoughts.
In 2010, John Gruber wrote The Future of the Mac in an iOS World for the Macworld back page. He explained why the Mac was still so important in the new world dominated by the iPhone:
It’s the heaviness of the Mac that allows iOS to remain light.
…
When I say that iOS has no baggage, that’s not because there is no baggage. It’s because the Mac is there to carry it. Long term — say, ten years out — well, all good things must come to an end. But in the short term, Mac OS X has an essential role in an iOS world: serving as the platform for complex, resource-intensive tasks.
It’s been fifteen years since he wrote that — have we reached the point where the Mac can come to an end?
I’d say not.
Apps like Final Cut Pro on iPad are an impressive achievement, but they lack the features, file management, and expansive screen real estate of the Mac. It’s a great tool for casual editing on the go, but I can’t imagine Apple saying that the Mac app is end-of-life without there being a huge uproar.
Another example is Xcode: even though the hardware on iPad and Mac shares the same processor, there is still no port of the user interface. And even if you can whittle the complexity of an IDE down to fit on a smaller screen, you’d still have problems with locking down the app. On the Mac, Xcode doesn’t run in an app sandbox and does not use the hardening that prevents certain security exploits. iOS mandates the use of both these things: it’s not even an option for app developers.
Xcode also sports 54 entitlements that let it do things other apps can’t. Things like authorization, device pairing, and inspecting the memory of other processes. A port of Xcode to iPad would immediately make it an attack surface for hackers.
So while iPadOS has obviously gotten more capable, I don’t see it displacing the Mac desktop as the place where the heavy lifting gets done.
Now let’s talk about that heavy lifting.
It’s done by professionals who have highly tuned desktop spaces and workflows. Windows and controls are in just the right place so focus can be wholly devoted to the task at hand. Everyone’s workspace is different and as unique as the person who created it.
If you’re someone who’s only using email, a web browser, and some messaging apps to get stuff done, changes to your desktop appearance aren’t going to be disruptive. It’s also likely that you’ll appreciate changes that make it look like your phone.
If you’re doing anything more complex than that, your response to change will be much different.
Have you ever wondered what the inside of a semi-truck looks like? Watch this short tour!Professionals on the Mac are like truck drivers. Drivers have a cockpit filled with specialized dials, knobs, switches, microwave ovens, refrigerators, and pillows that are absolutely necessary for hauling goods across country. Those of us who are making movies, producing hit songs, building apps, or doing scientific research have our own highly specialized cockpits.
And along comes Alan Dye with his standard cockpit, that is beautiful to look at and fun to use on curvy roads. But also completely wrong for the jobs we’re doing. There’s no air ride seat, microwave oven, or air brake release. His response will be to hide these things that we use all the time behind a hidden menu.
A beautiful Porsche 911 cockpit that’s perfect for high performance driving. But be sure to compare the angle of the steering wheel with the previous photo.It’s no wonder our reaction is somewhere along the lines of “fuck off”. Or maybe something a little more polite and eloquent. The bottom line is that one size does not fit all: we don’t want a Mac that looks or works like a phone, tablet, watch, or TV.
Worse, this situation is going to be like notifications on the Mac: a minimal design that mimics other platforms, and completely annoying in day-to-day use.
Liquid Glass is currently in a barely presentable state on iOS. It’s going to be like iOS 7 and take another year to sand down the rough edges. And then several more years to tone down the design, as with Aqua.
With the Mac typically lagging other platforms, I don’t expect to see any design improvements on my desktop for several years. It’s going to be an unpleasant and lengthy slog with various accessibility workarounds in place until the standard design looks decent. Or maybe, like with notifications, that will never happen because Alan Dye knows best.
My Mac has been a truck since the beginning of this century. When my desktop computer got Unix and Aqua it was the perfect platform to craft my cockpit. It’s going to be really hard to abandon that and create a new one, but the way things are heading, it feels likely.
I submitted the following feedback today. If you ever plan to change your business model from a paid up-front to freemium model, read this report and avoid a day of headache and stress.
Title: The sample code for a business model change was written by someone who’s never submitted an app to the App Store
Please describe the issue and what steps we can take to reproduce it:
The source code example using in Supporting business model changes by using the app transaction does not work if you’re using current Xcode and App Store conventions. Additionally, the sandbox environment uses the same outdated conventions.
And when you use that sample code, that you cannot test in the Xcode transaction simulator or in the TestFlight sandbox environment, it will fail spectacularly on launch day. You will be inundated with support requests from people who are expecting to see a payment for the previous version AND you’ll be in a state of panic because YOU HAVE NO IDEA WHAT THE HELL IS GOING ON. And did I mention that you can’t test this in production?
The sample code implies that the originalAppVersion is a string that’s separated by periods (“.”). The sandbox environment returns a value of “1.0” which reinforces this notion that it’s a value that separated by periods.
It is not.
If you’d read the Xcode documentation, you’d know that it automatically generates an app’s Info.plist. This has been the default setting for quite while – most developers have no idea this is a configurable option: they fill in the “Version” and “Build” number in the target’s General settings and are done with it.
For most, the build number will just be a single number that increments each time you submit to TestFlight (and eventually to the App Store).
When GENERATE_INFOPLIST_FILE is enabled, it sets the value of the CFBundleVersion key in the Info.plist file to the value of the build number (CURRENT_PROJECT_VERSION, or the “Build” in General settings). And that means your Info.plist is getting a CFBundleVersion without periods.
So what happens when you use this code?
let versionComponents = appTransaction.originalAppVersion.split(separator: ".")let originalMajorVersion = versionComponents[0]
Well, if you’re an inexperienced Swift developer, your app is going to crash with an array index that’s out of bounds. Those of us who are more careful in our receipt processing code will skip over the originalMajorVersion because versionComponents is empty.
And that’s when the emails from customers start arriving.
Luckily, there is this nugget of information describing originalAppVersion:
The originalAppVersion remains constant and doesn’t change when the customer upgrades the app. The string value contains the original value of the CFBundleShortVersionString for apps running in macOS, and the original value of the CFBundleVersion for apps running on all other platforms.
So even though CFBundleVersion was originally intended as a major/minor/patch format, its current use is as a single integer that increments when you submit to TestFlight. So the code above is expecting “1.0” and is actually getting “83”.
(And why the hell is it different on macOS? You do realize that cross platform apps are a thing, right?)
Again, you have no way to test this theory other than going through App Review (with an expedited review if you’re lucky). And if you’re even luckier, you’ll have folks on Mastodon that will confirm that this sample code is a piece of shit. A few hours later you’ll breathe a sigh of relief when folks start telling you that things are working fine.
And then the next day, you’ll write this bug report and post it publicly because no one else should have endure the stress caused by this sloppy code.
The addition of UITabAccessory in iOS 26 is welcome. It does, however, create a problem as far as backward compatibility is concerned. How do you present the new accessory view on older versions of iOS?
This backward compatibility is especially important for Triode. A lot of folks turn an older device into a dedicated radio player. I have an old iPad in the kitchen, for example.
So what do you do on the other side of the availability check where you set UITabBarController.bottomAccessory?
You’ll need to create two subclasses: one for UITabBarController and another as base class for all the view controllers you add as tabs (mine is creatively named as TabViewController).
In the UITabBarController subclass, you’ll do the check for availability in viewDidLoad and for versions older than iOS 26, you just add the accessory view to the tab controller’s view hierarchy using view.addSubview(accessoryView). You’ll also style the accessoryView as needed (e.g. adding a backgroundColor and cornerRadius). The same gesture recognizer is the attached to the accessoryView regardless of how you add it to the tab bar.
Then, in viewDidLayoutSubviews, you use tabBar.frame to position the accessoryView relative to the tab controls.
The other piece of the puzzle is doing the automatic inset adjustments on the tab controller’s views. In your common subclass (e.g. TabViewController), you’ll implement viewDidLayoutSubviews. On iOS 18 and earlier, you can check if the view or its first subview is an instance of UIScrollView. If it is, set contentInsetAdjustmentBehavior to .always and make new UIEdgeInsets to match the metrics you used in your tab bar controller.
(Side note: if you are having problems with the bottomAccessory on iOS 26 not animating as you scroll, make sure that your UIScrollView is the first subview. If something like a search field is the first view, it won’t work correctly.)
On iOS 26, Triode’s tab bar looks like this:
And thanks to the work above, folks on older systems can use the same accessory view:
You don’t get the fancy animations and effects, but folks on older devices will appreciate having the same capabilities. And better text contrast ;-)
Whether you love it or hate it, there is no shortage of opinion on Liquid Glass. I have thoughts about what it is, but today I want to focus on why it exists.
Apple’s public rationale for the new design language is that it offers a universal solution across platforms that takes advantage of recent hardware advances. Its touted benefits are a more lively experience that puts a greater focus on content.
The transition from iOS 6 to 7 was used as an example of how Apple has been successful with visual refreshes. But I don’t think that applies in this case.
In late 2012 and early 2013, there was a movement outside of Apple to simplify user interfaces: the highly textured designs introduced with the first iPhone had run their course. You can see this in our own work with Twitterrific 5 and other apps like Vesper. The launch of iOS 7 in 2013 was startling to some designers and developers, but not everyone. There was clearly a need, and the app ecosystem has benefitted from this change for over a decade.
I’m unaware of anyone outside of Apple who’s thinking “we really need to have more fluid glass in our designs”. Of particular note during the introduction is how much time they spend showing off glass blocks and talking about the physical effect itself. While not addressing the most important question: “why do we need this?”
And I’m pretty sure the answer is “we don’t”. The answer is “Apple does.”
I’ve spent the last few months updating Tot for iOS 26 while watching Sean do the same thing with Tapestry. One thing that’s clear from this work is that you never want a control or container that touches the edge of the screen.
It’s like when safe area insets appeared in iOS 11: it wasn’t clear why you needed them until the iPhone X came along with a notch and a home indicator. And then it changed everything.
There has also been an emphasis on “concentricity”. It’s an impossible thing to achieve and an easy target for ridicule. But it’s another case where Apple wants to take control of the UI elements that intersect with the physical hardware.
All of this makes me think that Apple is close to introducing devices where the screen disappears seamlessly into the physical edge. Something where flexible OLED blurs the distinction between pixels and bezel. A new “wraparound” screen with safe area insets on the vertical edges of the device, just like we saw with the horizontal edges on iPhone X.
The user interface work of the past few months will all make a lot more sense, and developers who haven’t been paying attention will have their “holy shit” moment.
I can see this new physical design being very successful with touch-oriented devices: it will feel natural with a phone, tablet, or watch. Hardware and software becoming one in classic Apple fashion.
I’m having a much harder time seeing how Liquid Glass will benefit other platforms like the Mac or Apple TV (where Apple doesn’t even make the screen). Forcing tactility where it’s not needed or wanted feels like a misstep.
Other challenges, like infusing your own branding into an app with clear buttons will be easier to reason about once the reality of the hardware drops. Until then, stay away from the edges and wait for Apple to reveal the real reason for Liquid Glass.
The first thing I installed after the WWDC25 Keynote was the beta for iPadOS. There was only one reason: it had the windows we have all wanted for so long.
And generally, windows on iPad work exactly how we want them to.
But there’s a problem, and I suspect that the root cause is that Apple engineers are thinking more about how the iPhone has worked for the past 18 years rather than how the Mac has worked for the past 41 years.
From the very beginning, iOS has had a notion of an app being in the foreground or background. When you saw an app on screen it was active and when it was gone it was inactive. The operating system let you know when that state changed and developers used it for all kinds of things:
It was simple system that let you do what you needed to do, when you needed to do it. Now with windows on iPadOS, that’s gotten a lot harder.
That’s because apps stay active even when their windows do not.
If you’re using iPadOS 26 and noticing that the saving/syncing/exchange of data is not happening, there’s a stupid trick you need to do to get things working:
Tap on the home screen to hide the windows (they slide off to the sides of the display). That makes all the apps on screen inactive and triggers the work that they need to do.
Of course, that’s a completely unintuitive action, hard to remember, and generally a pain in the butt. Especially when you’re on an iPad Pro with a lot of screen real estate and have several apps working together nicely.
(Note that this “hide to sync” issue is also a problem when you’re running iOS/iPadOS apps on your Mac: you have to hide a window to make it inactive. It breaks Tapestry.)
There is an API in iPadOS to track the state of each window: it has an “active appearance” that tells you when the window has focus. Unfortunately, on older versions of the OS, nothing is reported for this state, so backward compatibility is a problem. Also, on iOS 26, the active/inactive window state changes unpredictably while an app is off screen: my suspicion there is that the OS is taking screenshots to use in the App Switcher.
It’s taken several years for folks at Apple to arrive at a solution that the Mac had long ago, and I think it’s time for them to re-evaluate the notion of what makes an app “active”.
On macOS, two kinds of things can be active: an application itself or one of its windows. An app is active if it’s been launched and has at least one active window. A window is active if it’s frontmost on screen.
There is a clear distinction between the overall activity and the content-specific activity. If there is some global data that needs attention, you work with it when the app becomes active. If a window has some document data, you know to update it when the active state changes.
iOS and iPadOS need that same clear distinction.
For any Apple folks reading, take a look at FB18443571. It shows how I worked my way through this problem and came to the conclusion above. I’m happy to talk more about this problem and any potential solutions.
Updated June 16th, 2025: I’ve never been happier to be wrong about something as I was on the eve of WWDC25. History did repeat, but in a good way.
As with the iPhone SDK, Apple decided to open up its language models for all developers to use. This lets third party developers leverage all the features available on each platform: we are on equal footing.
Additionally, App Intents have become much more useful, thanks to their adoption in Spotlight. The use cases for human–oriented activities are much more concrete.
While I still have concerns about the business aspects of being a developer in today’s ecosystem, the technical situation is exactly where it needs to be. Well done, Apple!
I’ve been developing on Apple products for a long time: typing PEEK and POKE code from magazines into an Apple ][, figuring out how QuickDraw worked using the Inside Macintosh pre-prints, having my mind blown by Mac OS X and every new thing prefixed with “NS”, and then jailbreaking the first iPhone so I could write an app that eventually won an Apple Design Award.
It’s been an exciting adventure. Until now.
The engineering behind Apple products continues to be amazing: Swift and SwiftUI have made it easier than ever to create products. The App Store continues to be the easiest and most vibrant marketplace to sell those products (in spite of the company’s attempt to screw that up.) Fricken’ amazing hardware, too.
So what’s wrong?
The problems we’re solving and the apps we’re writing haven’t changed in years. After almost two decades of iOS, everything is iterative. And while maturity is a good thing, it’s not the thing that gets developers excited.
We’re at the point where a big change is putting a new coat of paint on our creations. Sure, it looks nice, and customers will love it. But it’s a lot of work and none of it sparks our imaginations.
But what is exciting these days?
Large Language Models: a huge body of statistical data that can be leveraged to solve problems that have heretofore been intractable. It’s the most exciting technology in decades because it lets our imaginations run wild and create new things.
And that’s a problem for developers in Apple’s ecosystem. Because while the company has done a significant amount of research with these models, and includes one on every iPhone, iPad, and Mac, the core capabilities of the mechanism are out of reach.
It’s like if Apple’s products didn’t provide direct access to the camera. There would be no Instagram, no Zoom, no Halide, just the Camera app. Developers don’t get a shutter button: they can only access photos that have already been taken. Apple knows what’s best for customers, of course.
Developers have been in this situation before: at the introduction of the iPhone. We all saw a wildly innovative piece of hardware that immediately gave thousands of developers a revolutionary idea for a piece of software.
Maybe it was emulating a glass of beer, turning the device into a musical instrument, a game that could only be played by touch, or a way to connect millions of people using photos and filters.
Then Apple told us we couldn’t write native apps and had to make web pages instead. There was no way for developers to do the same things Apple was doing. This was, indeed, a shit sandwich.
Eventually, the company came to its senses and opened up the platform, dropped the ridiculous non-disclosure agreements, and allowed developers to do what they wanted. That led to a period of innovation like I’ve never seen: developers had something revolutionary and magic happened.
Now history is repeating itself. We have a new shit sandwich that’s called Apple Intelligence.
Instead of building our own ideas on top of an LLM, we’re supposed to provide the internal details of our apps to Apple so they can do it on our behalf.
Providing those details is a lot of busy work for developers and not nearly as much fun as the coat of new paint: at least with visuals you can see and feel the results of your efforts. And from a business point-of-view, managing their internal details is why customers pay us. If Apple starts doing that on our behalf, what perceived value do we provide?
The internal details, called App Intents, are abstract and not something where you can immediately see the results of your efforts. It’s a “trust us Siri will be great at this” situation. Given the company’s track record in this area, there are few developers who think this will be successful. Worse, the improvements will be tied to lengthy release cycles: other companies drop language models with the frequency of new Emoji, not WWDC keynotes.
(I would not be surprised to learn that this whole situation is based on a fever dream of charging monthly service fees to use Siri and Apple Intelligence. These folks are seriously underestimating the reputational damage that Siri has incurred in the past decade.)
Some developers are working around this problem by providing their own models. This is unsatisfactory because it’s a waste of device resources: the downloads duplicate a tremendous amount of memory and storage. In many cases products are relying on cloud-based LLMs and losing all the privacy and security benefits of on-device processing.
All of this feels like Safari and mobile web apps in 2008: valiant attempts that everyone knows are wrong. Doing the best you can with a shit sandwich.
There are so many transformative ideas forming in developers’ minds right now that will never see the light of day. In our case, Tapestry has megabytes of textual information that describes a person’s interests and social connections. There’s no way for us to explore mining that data in a way that benefits the customer and respects their privacy.
(The developers who are making the greatest strides in this area are all doing it on the Mac. Ideas like Sky can thrive in a more open environment. Those of us in the jailbreak scene all saw how iOS borrowed heavily from its desktop sibling. Time will tell if that can happen again, but I suspect it will not given the locked down nature of mobile.)
So where does this lack of developer creativity lead?
It feels like developers are now part of the supply chain and being optimized accordingly. We are expected to refine and improve Apple’s ideas year-over-year. Our own needs and desires aren’t even secondary (where customers sit) or tertiary (our normal place in the hierarchy). We are just expected to deliver the products when Apple needs them.
I fear that this will lead to history repeating itself again, in a much more drastic way.
I remember how Microsoft’s response to the mobile revolution was to protect their existing desktop products. That looks a lot like Apple with its iOS franchise now. Instead of setting developers free, letting us experiment, and reaping the benefits, accountants and lawyers are fighting to keep us in line. We are all tired of the bullshit and many will happily move onto something better when it comes along.
Apple has been the lucky recipient of developer attention for a long time and they act like it will last forever.
It won’t.
We’ve recently released a new product. There’s no shortage of marketing or technical information about that.
What I want to talk about today is the fun we had making it.
Tapestry was a challenge on many fronts, but I’ve found that if you add a bit of humor and mischief to development, it helps get past the day-to-day frustrations you encounter. It’s hard to be pissed off when you’re laughing.
The spinnerIt all started with a fidget spinner. As we were getting our first beta release ready, Ged wanted a badge at the bottom of the timeline that said // BETA //. The initial release was functional, but there were a lot of rough edges that we knew needed smoothing. So a label there was.
On a Sunday afternoon I decided to have a little fun. A couple of hours later, our new badge recognized touches and had a very springy animation. And I didn’t tell anyone, not even my wife. That secrecy was hard, but the success of the gag depended on it.
But as soon as the people downloaded that first beta, we started getting comments like “I love the spinner!”. And no one on the company Slack had any idea what was going on until I said “tap the beta badge”.
Showing your first release to other folks is always full of surprises, even when it’s self-inflicted!
The spinner also ended up being used to test our error reporting mechanism. If you tapped it too often, which many people did, there was a message that you needed to ZAP the PRAM.
Yep, still having fun.
The discoOne of our beta testers, Joline Celebrion, is a huge fan of our iconography. More than once, she asked on our Patreon Discord about the arrival of alternate app icons.
A couple of weeks before they were ready, I added this bit of code to settings under the “App Icon” category:
I knew she’d immediately see the new category and open it excitedly, only to see a message that they were imminent. Teasing is only fun when you follow through, so in the next week’s build there was this footer below a large selection of icons:
And when she launched the app:
But we had to deal with that #warning and remove the message in the released product. And I knew it would immediately generate a bug report.
Good developers are proactive, especially when it comes to about boxes. And about boxes are branded with an icon. And on the factory floor, there is no shortage of icons. So I had my workaround: Joline was getting a disco.
The first step was to take all the icons and cycle through them to get a nice colorful flashing effect. That went out in a beta release and I hinted about it on Discord. Joline and everyone else loved it.
But that was just an amuse-bouche. I couldn’t close the bug report unless it had her name in it. I’d also been meaning to learn about the new TextRenderer modifier and protocol: I had my excuse to spend time learning and having fun.
Another important piece of the puzzle was knowing it was her tapping the icon. Luckily Kickstarter backers register their reward in the app so we had enough information to display everyone’s first name in the about box. I got to close a bug report and all our Kickstarter backers got a fun little bonus: that’s a win-win!
But it’s still Joline’s Icon Disco. She just lets everyone else visit and pretend otherwise :-)
And if you think these are the only Easter eggs, well, let’s just say that the best part of making software fun is watching folks discover the weird things we come up with!
Like if you find yourself tapping twice on the product website’s wordmark. Repeatedly!
I’ve been thinking about all the generative AI slop that’s appearing, especially with tools like “Reimagine”, and I think it’s going to be a great thing for the open web.
Why?
Because Google is unwittingly shooting itself in the foot in a way that will change the character of the web.
How?
The web has always been built on trust.
The very core of the Internet is built on trust: I try to connect and someone else accepts because I’m using a trusted protocol.
Trust is also an important part in the way people work together: a recommendation from a friend is a hell of a lot more important than any other media (including TV, print, and the web). We also negotiate once a level of trust has been established: just like our protocols.
That trust in people extended to companies that built their business on the Internet. We trusted Amazon to deliver our books. We trusted Google to deliver answers to our queries. We trusted The Onion to deliver us the lols. We trusted Twitter to connect with friends.
And all was good because it was built on top of trusted protocols.
Twitter was the first to break the human trust. Its popularity attracted a lot of bad actors: scammers, bots, and billionaires. And when things started falling apart, many lost trust in the service.
And just like a Nazi bar, when you can’t trust a place, you stop visiting. You find new places like Mastodon, Threads, and Bluesky that are welcoming with people you know and trust.
Now we have Google shitting in their own pool.
They are generating things that don’t exist. Pizza with glue. Pigs falling from the sky. Even Nazi SpongeBob.
As these generative technologies get better, you will be less likely to trust what appears in your search results. This change will happen at an exponential rate thanks to slop being generated from other slop.
Search engines you can’t trust because they are cesspools of slop is hard to imagine. But that end feels inevitable at this point. We will need a new web.
What?
The human component of the web won’t change. People will need answers that they can trust. Folks on the web are also resourceful; they always have been.
Something new will fill the gap and give people what they need and want. And my guess is that the open web, personal reputation, and word of mouth will be key components of that thing.
A better thing, thanks to slop.
This site now supports Dynamic Type on iOS and iPadOS. If you go to System Settings on your iPhone or iPad, and change the setting for Display & Brightness > Text Size, you’ll see the change reflected on this website.
This is a big win for accessibility: many folks make this adjustment on their device to match their abilities. Just because you can read a tiny font doesn’t mean that I can. It also is a win for consistency: my site’s font size matches the other text that a visitor sees on their device.
The best part is that this improvement can be realized with only a few lines of CSS:
html { font-size: 0.9em; font: -apple-system-body; font-family: "Avenir Next", "Helvetica Neue", sans-serif;}
What’s going on here?
The font-size property sets the default text size for the page. All browsers recognize this setting and so do you.
The new addition is the font property with the -apple-system-body value. This font is the key to getting support for Dynamic Type. This feature has been in WebKit for almost a decade and is fully documented. This property overrides the font-size that was defined in the line above and our page now has a size that matches the system setting for body text.
One unfortunate side effect of the font value is that it also sets the page in the system font. I like San Francisco, but I don’t want it on my blog.
With a hint from Mastodon, it occurred to me that I could override the face with font-family. So I now have the best of both worlds: a size that makes my visitor happy and a font that makes me happy.
One other addition that I made to my CSS was a tweak for desktop browsers. There is no Dynamic Type setting on macOS (yet?!) and the default size was a bit small for my taste. A @media rule fixed that:
@media screen and (min-width: 801px) { body { font-size: 1.2rem; }}
Now any browser window that’s wider than 800 points will get a slightly larger font.
You can, of course, use any of the other predefined font values, such as -apple-system-headline or -apple-system-footnote, but you’ll also need to override the family with each use.
But it’s likely that you’re already using em and rem sizes so that elements scale correctly in other contexts. By setting the base size in the html element, my rule for headers “just worked”:
.entry-header h1,.entry-header h2 { font-size: 1.4em; ...}
Another important point: if you’re using WKWebView or SFSafariViewController on an Apple platform, it will have the same capabilities as you’ve seen above. This means that you can have dynamic text in a SwiftUI view and a web page that matches exactly. This is why I needed to solve the problem in the first place.
Take a moment to look at your blog, product, or company style sheet and think about how this approach to accessibility can improve things. If you’re like me, in a couple of hours you’ll have a much better site.
A toot by my friend Casey brought back some frustrating memories about expired subscriptions that haven’t expired (yes, really). This blog post will hopefully help you avoid having these same recollections.
It all begins when a customer contacts you with a screenshot that looks something like this:
Your code and the App Store don’t agree about when a subscription expired. The cause of this is Apple’s StoreKit sample code. It’s likely that you have some code similar to line 246 of Store.swift:
subscriptionGroupStatus = **try**? **await** subscriptions.first?.subscription?.status.first?.state
That code will work fine until you encounter a customer that has Family Sharing enabled, as most do. The issue is that the Product.SubscriptionInfo can contain multiple items, and the code above only checks the first one.
How can that happen? With Family Sharing, the people who are using the subscription act independently: one may subscribe for a year and then cancel. Then another could subscribe at a later date for only a month. You have to check all of the subscriptions, not just the first one. Something like this:
**if** **let** statuses = **try**? **await** subscriptions.first?.subscription?.status { **let** checkStatus = statuses.first { $0.state == .subscribed || $0.state == .inGracePeriod } ...}
The documentation and sample code doesn’t say it, so I will: Apple’s StoreKit sample doesn’t support Family Sharing.
If you’re looking for code from Apple that does support Family Sharing, you can find it buried in one of the WWDC demo apps. Obviously.
What’s most frustrating about this situation is that you know it exists if you’ve read the documentation:
The array can have more than one subscription status if your subscription supports Family Sharing. Provide the customer with service for the subscription based on the highest level of service where the state is subscribed.
Which makes no sense until you’ve read the paragraphs above.
Actually, I was wrong. The most frustrating thing about this situation is that it’s essentially untestable. You can’t reproduce the problem, even after a customer lets you know they’re having issues and you’ve read this blog post. That’s because there is:
The StoreKit test harness in Xcode has been a godsend, but in this case it’s just not up to the task. And the result is lots of frustrated developers who are testing code in production on a customer’s device.
Apple folks: you can learn more in FB13212468. It’s been closed as “Investigation complete” — maybe you should ask Casey if he agrees with that resolution.
On Mastodon, Alex Chaffee points out some of Tapestry’s shortcomings. These are all valid concerns and I’ll deal them individually here (rather than with a long toot thread).
iOS-onlyBuilding for iOS first is a strategic choice. There is a lot of work to do here, and many new concepts that need to be designed and developed, so we’re picking our battles carefully.
Personally, I’d prefer to do a macOS client first, but iOS is a choice that makes the product available to as many folks as possible.
We also get asked a lot about supporting Android and that’s something, like macOS, that we will look at after we have a strong footing with this new concept.
But what about the web?Another platform we get asked about frequently: can Tapestry be a web app? We covered this in a FAQ, but I’d like to add a little more detail here.
The only way a web app could be done is if there’s a server to marshal requests to the various services. Since we want the privacy and independence of a device-only design, that option is off the table.
If you’re working in a single browser window as a web app, you put all of your code into a single secure JavaScript context. That’s not a problem, but any code you pull in will need things to communicate with all of the services, and in most cases that will leak private authentication data like OAuth/JWT keys and tokens. All it takes is one malicious plug-in to make your life hell.
The root of the problem is that Tapestry’s fundamental design is different than a web browser. The easiest way to think about: it’s an app that asks a bunch of browser tabs if they have anything new to show. Each tab is secure and none of them know what the others are doing. The results that each tab provides are aggregated and displayed to the user.
This design is wholly different from the web we’ve known, but is just as flexible and adaptable. Building a new thing for the community of the open web is what excites me most about this project. Giving folks tools to be creative is what the web has always been about.
Closed sourceRight now, the core of Tapestry is closed source. We have put some components up on GitHub and are also fully documenting an open API to that proprietary core. Teaching is a part of that openness.
Again, we are in the very early days of this project and have no idea where things will head. It’s like if you had asked me in 2006 if a shithead would be running the show in 2024.
I think it’s important to think about how Netscape Navigator was a proprietary product until Mozilla happened. Establishing a new idea is a first step; letting it flourish is another.
All I can say at this point is that I’m fully committed to letting this idea flourish.
ScrapingThere is no such thing as a proper web scraper for authenticated content. It’s a cat-and-mouse game where the scrapee wins in the end. We have every reason to believe that Facebook or Instagram will go to great lengths to protect “their” data.
On the other hand, if you want to get standardized information from a page, such as OpenGraph, that’s already something we’re doing.
PostingThe prototype I built (named Muxer) was able to post to Mastodon and Micro.blog. But it’s a harder problem than you first think.
The issue is that each service has a different set of capabilities. On Bluesky, you can’t post video. On Mastodon, you can post polls. With RSS, you can’t post at all. Size limits and file formats differ wildly (including between Mastodon instances).
You quickly end up in a situation where a user interface gets confusing. For example, you have a video ready to post on Mastodon and decide that you’d also like to send it to Bluesky. As soon as you do that, the post button gets disabled and it’s hard to explain to a user why that happened.
It’s also not clear in my mind if cross-posting between services is a good thing or not. Once you have an app that can display information from many different sources, it quickly gets annoying to see duplicates.
As we learn more about this product, and what people want from it, we’ll have a better idea of how to handle posting.
And more…I’m always happy to talk about Tapestry and explain the thought processes behind it. If you have questions or concerns not covered here, please feel free to reach out on Mastodon.
And, of course, everyone at the Iconfactory would love your support for Project Tapestry.
Last week’s 40th anniversary of the Mac got me thinking. I’ve also been contemplating this week’s release of Apple Vision Pro.
It feels like we’re at a crossroads for platforms, but one that’s impossible to pass.
I was one of the folks who bought a Mac in 1984. At the time I was a member of a team building a Unix workstation from the ground up. We had bigger displays, better networking, faster processors, more memory, and larger disks.
But we were all jealous of what the team at Apple had done. That first Mac and its system software was brimming with new user interface ideas and techniques. Better ways of doing everything we had done.
And you can say the same thing about Apple Vision Pro and visionOS.
Except there is a problem.
ProcessesIf you’re a software developer, the Apple Vision Pro cannot be used standalone for your work. You’ll be able to use it as much as you do an iPad. You can experiment in Playgrounds and build some simple apps, but you’ll quickly hit a wall.
That’s because developers use a lot of processes. And these processes talk to each other in very creative ways. Maybe it’s as simple as creating child processes to handle work. Maybe it’s a more complicated process like a Docker container running a web server that talks to a database process via a Ruby on Rails process. There are processes everywhere you look.
And in an Apple sandbox, you get one process. You can’t fork and exec a child. And if you query the Mach kernel for information about another process, you get back KERN_FAILURE.
(To get a very good idea of what’s possible at the fringes of a sandbox, take a look at a-Shell on your mobile device. It does an amazing number of things, but you’ll quickly feel frustrated that ps, kill, top, and anything else that deals with processes is missing.)
There is a good reason for apps only having visibility of their own state. Imagine the kind of fingerprinting that Google and Facebook could do by seeing what apps you’re using. We’ve already seen apps trying to do the same thing using URL schemes.
When my pal John Gruber talks about Macs doing the heavy lifting, it’s not just about complex and resource-intensive tasks. It’s also about the security exposure: the Mac is the only “dangerous” Apple platform.
WindowsThere is a thing that developers love almost much as processes: windows. We have so damn many. Hundreds on a good day. Thousands on a really good day.
And this is why I get frustrated every time I see a demo of Apple’s headset. I can easily imagine fitting my work into a space with an infinitely large interaction surface.
As it is now, you get to see a screen or two streamed from your Mac. That will surely improve; probably to the point where you have individual windows in your spatial environment.
But you’ll still be carrying the Mac around to get any work done. Somewhat ironically, the Apple Vision Pro is not doing the heavy lifting, but it will be the thing that’s cumbersome in your daily life.
Here’s a comparison of the headset’s carrying case and a MacBook Air:
11.69″ × 8.78″ × 6.5″ vs. 11.97″ × 8.46″ × 0.44″The Apple Vision Pro is almost 15 times taller than the MacBook Air. Even worse, I can’t even close my backpack, much less fit in a laptop:
“You’re going to need a bigger boat.”After the Mac was introduced, you didn’t have to carry around an Apple ][ or Lisa to do software development.
Yet here we are because the Apple Vision Pro is locked down. It’s being relegated to being a fancy display for software developers. That’s not necessarily a bad thing and there’s no extra cost for a display stand.
But…This isn’t a sustainable situation for the next 40 years. Without some low-level structural changes in visionOS, it will never thrive as a developer platform. Just as the iPad has not.
It also doesn’t bode well for the Mac. I’m sure Apple can continue to add incremental changes to satisfy developers, but there won’t be anything revolutionary with how we work. There is also little incentive for Apple to change here: you are buying an Apple Vision Pro along with a MacBook, after all.
One of the extrordinary things that happened back in 1984 was the ability to have more than one terminal window. Even though my Mac had to be connected to a VAX 11/780 over a serial cable (sound familiar?), this was a completely new way of working. We were suddenly free of working within the confines of a single 24×80 character display.
Once we broke free of those limitations, things like visual development environments took hold. I’m pretty sure Apple understands the productivity benefits that came along with these changes.
And here’s the thing: developers don’t come up with these ideas unless they have a place to experiment. Seeing multiple windows that contained code, debugging, and other tools led some folks to start thinking about integrating this environment using the new interaction mechanisms.
Those same kind of folks may find inspiration in spatial computing, but will ultimately get thwarted by the restrictions of a single process. An architecture developed for mobile devices with only one app on the screen is now being used for apps on an infinitely large screen.
Apple Vision Pro is a technical marvel, but ultimately falls short in ways that satisfy the natural curiosity of developers.
That’s a shame. I just hope some smart folks at Apple feel the same frustration I do, because we need a future beyond the Mac.
The new visual appearance and functionality of watchOS 10 is a welcome change. There was clearly a lot of design and engineering effort put into this new interface and the improvements are tangible for most apps.
Unfortunately, the app that I use the most on the Apple Watch has lost much of its usability, both in functionality and accessibility.
I’m talking about the Timer app.
The team designing watchOS clearly knows what it’s doing. Using the infinitely large corners of the Apple Watch display to leverage Fitt’s Law shows remarkable insight. The new gestures, while unfamiliar at first, feel like they will be as transformative as when phones no longer had Home buttons.
The only explanation I can find for the Timer’s design regressions is an unfamiliarity with some use cases. In the following critique, I’ll focus on how the watch is used in the kitchen and how older customers struggle with the new layout. Suggestions will be kept to a minimum: the effort here is to be descriptive, not prescriptive.
HistoryThe Timer has been my favorite app since it debuted in the first version of watchOS. It was basic: you could only set one timer and you had to do it manually (there were no presets).
Why was this interface so useful to me? Because I spend a lot of time cooking.
My watch became the perfect timer. It went with me wherever I was making a meal. No more situations where you set a reminder in the kitchen and don’t hear it go off because you are outside at the barbecue.
The next version of the Timer added presets for 1, 3, 5, 10, 15, and 30 minutes along with settings for 1 or 2 hours. This, to me, was the pinnacle of its design on watchOS. Why?
Those time settings, placed in a neat grid, provided all the functionality I needed. I only used the first six timers, but I used them often.
But more importantly, the navigation of that magic grid could be done without hands. Any cook can tell you that there are times where uncooked food on your hands is either dangerous or messy. Dressing a chicken, gutting a fish, or making meatballs are all times where you will not want to touch an Apple Watch.
Instead, you will navigate with your nose.
That’s right, a capacitive screen works with any part of your body. I can easily start a new timer or cancel a ringing timer just by holding it up to my face. And when you’re cooking, you do that often.
Which leads to the next interation of the watchOS Timer. The addition of Recents made getting to the 1/3/5/10/15/30 settings more challenging because scrolling with your nose is significantly more difficult. Thankfully, once you positioned the magic grid on the device, going into and out of timers could be done quickly and easily.
And the efficency of selecting a timer is very important when you are setting a dozen of them every hour. How can that happen?
Cooking TimesSo now let’s look at some of the specific things that cooks need from the Timer app. To give you an idea of the basic challenge, you can ask this simple question:
How long does it take to carmelize an onion?
The correct answer is: “I have no idea”. There are too many factors involved:
What you do know is that it will take about 15 minutes for the onion to soften up at a medium heat. And then you check it. And probably lower the heat and set a timer for 10 minutes. And check it again. And then probably do a 3 or 5 minute timer on low heat a couple times. And when that’s done, you go with one minute and do your best to not burn them. This is how you end up setting a dozen timers in an hour.
All cooks have this inherent knowledge: when a recipe says “15 minutes” you know that means “check it at 10 minutes” and go from there. I don’t even trust times on frozen pizzas: are you absolutely sure that your oven temperature is 425º F?
This is exactly why the magic grid in the Timer app is so important. It has all the common checkpoints a cook needs. It’s also why Recents are a non-feature while cooking: after you’ve done your 5 minute checkpoint and go back to Recents you need a timer for one minute but 5, 30, 15, and 3 are the most recent.
Another aspect to cooking is that you’re typically in a hurry and juggling multiple tasks. Setting a timer manually is much quicker and safer than relying on Siri. A kitchen also tends to be a noisy place, with multiple conversations and background music. If Siri doesn’t understand what you’ve said, you’re going to end up with burnt onions.
Growing OlderThe challenges of growing older are numerous, but the one that I struggle with the most is vision. My eyes suck.
If you’re a young designer, you have no idea what’s coming. I didn’t.
It’s common for aging eyes to be affected by presbyopia. The focal difficulties associated with this disease means you have to concentrate more to read small text. That is exacerbated when the text contains repeated symbols. The contrast of the text against a background is also important.
In short, symbols like “01:00” and “10:00” increase cognitive load because they can’t be read at a glance.
Also of note: your health is a much bigger concern as you grow older. You are reminded of your mortality every day when you struggle to put on your socks. The Apple Watch’s health monitoring features become essential as you enter this stage of your life. I’m confident that Apple has a lot of aging users for this device.
It’s likely that the problems faced by a large portion of the customer base aren’t known by a young design team. I’m hoping this essay will help with that.
The ComparisonNow that we’ve covered some of the specific needs of old farts and people who like to cook, let’s look at how changes in watchOS 10 affect both functionality and usability for these groups.
As we discuss these changes, I’ll be referring to the image below which shows how things looked before (left) and after (right) the new watchOS version:
The interface for picking a timer on watchOS 9 (left) and watchOS 10 (right)FunctionalityThe root of the problem on watchOS 10 is that setting a timer is now done in a modal presentation (with a close button in the upper-left corner).
This means that no position is maintained between uses: the Recents are always at the top and have the ordering problem noted above. The magic 1/3/5/10/15/30 grid is only accessible by scrolling.
The new UI is impossible to use hands-free. Cooks who want to set or change a timer while deboning a chicken are out of luck.
This problem could be remedied if the last position in the modal view was maintained as in previous versions of watchOS. If maintaining the scroll position is not possible, an affordance to remove or collapse Recents would help by putting the magic 1/3/5/10/15/30 grid at the top of the view.
Additionally, the All Timers list begins with a big plus button. This breaks the muscle memory associated with 1 minute being in the top-left position – it’s now in the top-right.
In my experience, adding a timer is a rare occurance. I’ve done it twice in the 8 years I have owned an Apple Watch. Both were for laundry: one for a washing cycle and another for a drying cycle.
Putting the big plus button at the end of the list, and closer to Edit, feels like a better solution that makes the list more readable and familiar.
AccessibilityWhen you compare the screenshots of the previous and current versions you can easily see that watchOS 10 is more consistent. The list is also shorter because favorites have been folded into All Timers. These are both good things.
But the consistency works against me and my failing eyesight. Everything looks the same and it’s difficult to know where I am within the list. (Remember that you only seeing four of the circles on the watch face: there are points while scrolling where you can’t know if you’re in Recents or All Timers.)
As noted above, differentiating between “01:00” and “10:00” takes more effort. Not only is the text smaller, but there’s a lot of extra noise that affects readability. The text in watchOS 9 used “1 MIN” and “10 MIN”, with the numbers presented in an accent color to emphasize the minutes. It was much more readable.
The need for leading and trailing zeros to maintain consistency also leads to a situation where timers that are longer than 59 minutes get smaller text that’s harder to read. Luckily, the need for a 10 hour timer in my life is unlikely, so I don’t have to deal with reading a tiny “10:00:00” and “01:00:00”.
The leading and trailing zeros made some sense in watchOS 9’s Favorites and Recents list where each button’s label aligned well with its siblings. But with the grid layout in watchOS 10, the need for zero padding is not necessary and just feels like visual noise.
(Note that users with VoiceOver hear “one minute”, not “zero one colon zero zero” or “one minute zero seconds”. Visual noise for normal eyesight should be reduced just as it is with the spoken audio.)
My first thought was that these changes on watchOS were an effort to get consistency across platforms, especially with the addition of multiple timers in iOS 17. This does not appear to be the case:
The Clock app on iOS 17 showing how timers are formatted in various contexts.I have also wondered why seconds are shown as a part of the standard format on watchOS. I’m sure there are people that set timers for 12 minutes and 34 seconds, but they are certainly the outliers. When was the last time you had a pizza that needed 11:45 in the oven or a load of laundry that took 45:11?
People think in minutes, so minimize their cognitive load by focusing on that unit of time. By doing this, you could improve accessibility for everyone by using BIGGER NUMBERS on a small screen.
(Seconds could be handled in a way similar to the new Modular Ultra watch face. Dimming the trailing seconds would allow someone to know that “1:23” is one minute and 23 seconds and not one hour and 23 minutes. Again, minutes are the thing that’s most important to people.)
In summary, this is a rare case where less consistency would make for a better and more accessible user interface.
One final accessibility problem in the new Timer app is when one completes. Here is what you see, both with and without accessibility features turned on:
Bold text in the default size (left) and with increased contrast and size (right)The image on the left has bold text turned on with the default text size. On the right, you see what happens when you make the text size larger and increase the contrast.
The timer length on this screen is not accessible and cannot be improved with accessibility settings. The dim text on a bright orange background is incredibly hard for me to read. I think the information hierarchy is the root of the problem.
“Done” is not the most important thing on this screen when you have a timer that completes: the length of what just finished is most significant. People will know what a bright orange screen and ringing means. They may not know which timer fired, and that can only be determined by reading “1 MIN” in dimmed out text at a much smaller size.
While counting, the current state of the timer belongs in large white text. When the count is complete, the length of the timer that finished has that same level of importance. This significance increases when you have multiple timers.
(Confusing timers with similar lengths is an easy thing to do: I nearly screwed up two COVID tests at a time when they were in extremely short supply. This happened because I didn’t notice the “5 MIN” and “15 MIN” text.)
At least the timer text in the screenshots above is not “01:00”. Please don’t make this consistent, too.
ConclusionI’m honestly not looking forward to the next year with the Timer app. It’s going to be an irritation dozens of times every day.
My only hope is the other great improvements in watchOS 10, especially with workouts and activity tracking, will make up for it. 🤞
If you need to get in touch with me about any of this, there’s FB13212554. If you’re a developer who feels similarly about these regressions, please dupe this feedback (it’s a copy of this blog post). Otherwise, you can send your comments to Apple directly.
There’s a new “feature” in Sonoma, and no one besides Apple is quite sure what it is. Alerts for deprecated APIs are now appearing frequently. Sometimes when you launch an app, and sometimes at random. Here are three I got the other day after waking a MacBook from sleep: From a UI point-of-view, these alerts […]
I’m happy to announce the release of a new tvOS app called Blank. It turns your screen black and keeps it that way until you press any button on a remote. Seriously, that’s all it does. Here’s the screen you see when you launch the app for the first time:
That second paragraph hints at why this is important, despite the app’s simplicity.
Sleeping WellAs you get older, a good night’s sleep becomes harder to achieve. One thing that works well for my wife and me is to lower light levels before bedtime.
There has been scientific evidence of this since early in the last decade. Bright light can change your circadian rhythm and affect melatonin production, which can “potentially impact sleep, thermoregulation, blood pressure, and glucose homeostasis.”.
A big ass screen in the living room makes this hard to achieve. If you want to listen to music or a podcast before going to bed, it’s impossible to avoid a bright now playing screen or animated screen saver.
So I wrote Blank as a way to address this problem. Of course, it’s FREE so people besides me and my wife can benefit from it.
New & ImprovedThe first version I submitted didn’t meet Guideline 4.2 for “Design – Minimum Functionality”. Understandable, because this app was basically the “anti-flashlight” and we all know how that played out.
I took this initial rejection in stride and started working on an update that added some minimal functionality.
When you launch the app, or press any button on the remote, you get a screen with an inspirational quote. After you’ve had time to read it, the message disappears, and the screen goes black. It’s a nice addition and folks who are using the app love it.
I’m glad I did this extra work, and it’s a case where App Review helps a developer improve their product. Here’s what the quote screen looks like:
After some back-and-forth with App Review, the app was approved with these changes. Yay!
But That’s Not All!An additional benefit became apparent after we started using Blank: it significantly lowers the energy consumption of the screen.
All modern TVs have circuits that detect a blank signal and turn off LEDs to reduce the power required by the device. If you’ve ever felt heat coming off your big screen, Blank makes that go away.
So besides improving your sleep, you’re also helping out our ever warming planet.
There Is None More BlackSo there you have it: another addition to our ever growing list of “little apps”. Just open up the App Store on your Apple TV, search for Blank, and click to download the app for FREE.
Simple, beautiful, classic. Enjoy!
Well, it happened.
We knew it was coming.
A prick pulled the plug. And what bothers me most about it is how Space Karen did it.
My mom passed away just before Christmas. Her decline was something everyone in the family saw coming and we prepared for her demise. It still hurts like hell, but she left with love and dignity. That makes all the difference when it comes to coping with loss.
Twitterrific is something that we’ve all poured our love into for the past 16 years. I’m not usually one to toot my own horn, but we literally crafted the early experience on the service. We often hear that folks joined up because of our app. Our work was definitive and groundbreaking. We loved this app like I loved my mom.
(Note today’s date and the one on our announcement – the fuckwads missed our 16th anniversary by a couple of days! King Shithead probably thought Friday the 13th was lol. I’d love some proof that the API went down at 04:20 in UTC +1.)
Like my mom, the API has been declining for awhile. Endpoints were removed, new features were unavailable to third parties, and rate limiting restricted what we could do. And like my mom, we struggled on and did the best we could, trying to stay upbeat about it all.
What bothers me about Twitterrific’s final day is that it was not dignified. There was no advance notice for its creators, customers just got a weird error, and no one is explaining what’s going on. We had no chance to thank customers who have been with us for over a decade. Instead, it’s just another scene in their ongoing shit show.
But I guess that’s what you should expect from a shitty person.
Personally, I’m done. And with a vengeance.
First, arrogant bastards love seeing their names on tweets and other media. I want to starve him of the things that money can’t buy: respect and attention. Do the same by simply ignoring him and his kingdom.
Secondly, for the past several months I’ve been thinking about where we go from here. When you see decline, you plan for a demise. It was the last thing mom taught me.
I’ve been active on Mastodon since the billionaire bozo took over. And it makes me think.
One thing I’ve noticed is that everyone is going to great lengths to make something that replaces the clients we’ve known for years. That’s an excellent goal that eases a transition in the short-term, but ignores how a new open standard (ActivityPub) can be leveraged in new and different ways.
Federation exposes a lot of different data sources that you’d want to follow. Not all of these sources will be Mastodon instances: you may want to stay up-to-date with someone’s Micro.blog, or maybe another person’s Tumblr, or someone else’s photo feed. There are many apps and servers for you to choose from.
It feels like the time is right for a truly universal timeline. That notion excites me like the first time I posted XML status to an endpoint.
One thing I remember from these early days: no one had any idea what they were doing. It was all new and things like @screen_name, #hashtags, or RT hadn’t been invented yet. Heck, we didn’t even call them “tweets” or use a bird icon at first! The best ideas came from people using the service: all of the things mentioned above grew organically from a need.
That’s where I want to be in the future. Exploring unknown territory that empowers others and adapts to the needs of a community.
There’s no sense in clinging to the personal whims of a clown leading a shit show. Especially when his circus will end up being a $44 billion version of MySpace.
Have you ever added code like this to your app?
print(Bundle.main.resourcePath!)print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.path)
Or maybe you’ve been frustrated that you can’t add that code because you’re in the middle of debugging?
Yeah, me too. Many times.
The locations show above, and many others, are available from Xcode using the xcrun simctl command. Every application on every device on every platform can be queried. But these lookups are difficult for developers because the information is structured around automatically generated GUIDs. The GUID you’re looking for changes every time a new OS is available, a device is added, or an application is installed. And we do that a lot!
There are other tools available to help you navigate the Simulator, but they all do much more than I really need and take up space in my menu bar even though they are used infrequently. Additionally, none of these tools help find the “On My iPhone/iPad” container used by the Files app: a folder that I use whenever I’m testing import and export code.
By now, you probably know where this is going: yes, I wrote my own utility and call it SimBuddy. It’s a FREE download from the Iconfactory.
SimBuddy uses two popup menus for navigation: the top one shows which devices are running in the Simulator and the one below shows all the applications installed on that device (your apps are listed first). Once you make a choice with those popups, you can use the buttons at the bottom of the window to navigate in the Finder. If you are using app group containers for sharing information between an extension/widget and your main app, you open those folders by selecting the ID and using “Open”.
If the Terminal is more your thing, you can hold down the option key while clicking a button and a path to the folder is put on the clipboard. Paste that into a command line and away you go!
It’s not a complicated app, as you can see from the source code, but it’s one that I’m very happy to have in my developer toolbox now. I hope you enjoy it, too!
P.S. I love putting Easter eggs in apps. This time it’s in the app icon.
Beginning with Xcode 14, the Simulators for watchOS and tvOS are available as separate downloads (iOS and macOS are still “built-in”). This reduces the app download size significantly, but it also means that you now have to manage these large (3-4 GB) components yourself. When you launch Xcode 14 the first time, you are prompted […]
It’s been awhile since I’ve done one of these deep dives on what goes on behind the scenes during the development of an Iconfactory app. There’s a common thread to each one: I feel the need to document our work when there’s a major change in how we build user interfaces. The first one was […]
Where there’s smoke, there’s fire. And as we approach WWDC 2022, there’s a lot of smoke around AR and VR. In some ways, this is going to be a huge inflection point, in other ways, it’s probably going to be a letdown. Remember when the iPod was announced? Some folks called it lame because it […]
This past summer we narrowly avoided a major user interface regression on Apple devices. The story ended well, but I think it’s important to look back on the situation and ask a simple question: Why did this happen in the first place? My answer is something I call “consistency sin”. Understanding the cause lets us […]
In my ongoing quest to release FREE apps that are useful in my daily life, I’m proud to announce the release of AV Remote. It’s a very simple remote for Denon and Marantz AV receivers. The official apps for controlling these receivers work fine, but have way too many features for my needs. I don’t […]
A friend of mine recently pointed me at a well hidden command line tool. In the JavaScript framework used by Safari and other parts of Apple’s products, there is a tool called jsc. It’s a command line interface for JavaScript that uses the same code as the rest of the system. You can find the […]
It’s common wisdom that you should release a software product when it is minimally viable: get an early version out in the world as soon as it can perform a useful task for a customer. When that product is for someone who is a developer that’s been coding since the dawn of time, the equation […]
I have a long history of writing about code signing in macOS. When Big Sur was released, I thought “Finally!” I was wrong. This time around I was tripped up by Safari, of all things. It doesn’t open app archives like other parts of macOS. This story began with customer reports of xScope being a […]
Today, the Iconfactory is happy to introduce a product that’s made with three different apps: Triode. Simply put, it’s the best way to enjoy all of your favorite Internet radio stations, wherever you go. So why did I make these apps? A Brief History of Internet Radio Before I can answer this question, it helps […]
Luckily, I don’t have to use this kind of title often. But when I do, there’s a good reason: this year’s beta release cycle for all of Apple’s operating systems has been a mess. The months since WWDC in June have been a terrible experience for both customers and developers alike and the literal center […]
We recently started updating our macOS apps for Catalina: so far there have been very few issues with APIs and frameworks. The biggest hurdle has been the new notarization process that’s required for apps signed with a Developer ID: customers will be unable to download and launch your product easily until this step is completed. […]
I am so fucking excited about SwiftUI. I had strong suspicions that Apple was working on something like this, but what we’ve seen this week goes way beyond any of my expectations. My thoughts have now turned to how important this development will be for the future of all user interaction. It all started while […]