The Year That Kicked My Ass

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.

UITabAccessory Backward Compatibility

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 ;-)

Making Software Fun

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 spinner

It 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 disco

One 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!

Announcing Blank

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 Well

As 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 & Improved

The 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 Black

So 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!

The Shit Show

Well, it happened.

We knew it was coming.

A prick pulled the plug. And what bothers me most about it is how Phony Stark 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.