I don’t recall when, exactly, I started using Dropbox. However, I apparently earned a bonus 250 MB of space by completing the “Getting Started” flow back in September of 2009. So, it’s been a minute.
At first, Dropbox was a revelation. It was the first time something synchronized with the cloud, and did so reliably. Their client apps were svelte, optimized, and worked a treat.
Over the years — mostly but not entirely because of their own choices — their client apps have become gross, bloated, overcomplicated messes.
I still rely on Dropbox to collaborate with my coworkers. However, I haven’t run one of their client apps on my computers in around five years. How?
My Synology takes care of it for me. If you happen to have a Synology NAS, you, too, can live in this eden.
The path forward is a combination of Synology Drive and Synology Cloud Sync. In short, Drive acts as a faux-Dropbox, and allows you to sync files between your devices via your own Synology. Cloud Sync then synchronizes between your Dropbox and your Drive.
So, for me, my Synology’s filesystem looks like this:
~casey
+-- ...
+-- Drive
| +-- ...
| +-- Dropbox
| | `-- (My entire Dropbox is here)
| `-- ...
`-- ...
On my Mac, it looks basically the same.
So, if I were to create a new file on my Mac, and save it as
~/Drive/Dropbox/some-text-file.txt
The following will happen:
Dropbox
directoryThus, some-text-file.txt
ends up in my Dropbox, and all I had to do
was save the file on my local SSD and trust my synchronization scaffolding
to take care of it.
I will say that the Synology Drive client for macOS is… not great. It’s clearly a cross-platform app, though I believe it’s using Java or some other sort of technology, rather than Electron. It’s ugly, but it works, and unlike the Dropbox client apps, it stays out of the way.
I can’t recommend this setup enough, and if you have the means Synology, I
highly suggest you pick one up give it a chance.
I was pleasantly surprised — moments before sitting down to record Clockwise — that Devon Dundee over at MacStories had done a write-up of the aforementioned Callsheet 2025.2. Specifically, Devon spent a lot of time with the multiple lists of pins headline feature.
Outside of the publicity, this write-up was particularly gratifying for me, as Devon absolutely took notice of some of the power-user affordances I included. I thought long and hard about how to appease both novice and power users alike; it was so great to hear Devon pick up on all of these choices.
This includes both the progressive disclosure of multiple lists[1], as well as the surprising depth of the pin toolbar icon[2].
One of the ways I stay positive about my work is by working hard. I try very hard to produce work I’m proud of, and that will delight my users. To be able to read about how Devon was delighted by a very large effort that took a lot of thought was such a gift. 🥹
If you haven’t given Callsheet a try recently, you really should. It’s really coming into its own. 😊
Namely, the switch from Add list…
to Switch list
on the Discover screen. ↩
If you tap on the pin, the current item will be added or removed from the active list, as appropriate. If you tap-and-hold, a menu will be presented, which will show all lists, and whether or not the current item is a member of any of them. You can quickly add or remove from this menu. Finally, the pin will be pinstriped if the current item is pinned in a non-active list. ↩
Yesterday I joined Dan, Micah, and Simone de Rochefort on Clockwise.
On this episode, we discussed what we carry in our wallets, whether or not buying a phone made by a heavy equipment manufacturer is a wise choice, how we feel about the new Pebble, and how my co-hosts are all wrong about cellular MacBooks.
]]>Upgrade is my favorite tech podcast. I enjoy it thoroughly, every week.
This week, I was honored and lucky to fill in for Myke and join Jason on the show. It’s a lot of pressure to join one of your favorite shows; not only did I not want to let Jason and Myke down, but I didn’t want to ruin a great show!
Thankfully, I’m really happy with how the episode turned out. Jason and I spoke about all sorts of things. Naturally, a lot of Apple news, but I was happy to sneak in some of my latest obsession — home automation — as well as an update on Callsheet.
Jason and Myke typically film the show and upload to YouTube; Jason and I did the same. You can find the episode there:
If you’re not a regular Upgrade listener, shame on you, but there’s no better time to start than now.
I’d also like to take a moment to wish a hearty CONGRATULATIONS to Myke and Adina Hurley! Myke needed me to sub for him on Upgrade for a very good reason:
Welcome to the world, Sophia. Your parents worked hard to get you here, and I’m very excited to meet you.
]]>Since shortly after Callsheet was released, my #1 request from my users has been the same thing: pins are great, but can we have multiple lists of pins?
Today is the day.
Over the last few days, Callsheet 2025.2[1] has been quietly rolling out to users. While there is a lot in this release, the headline feature is the ability to make multiple lists of pins. If you have Callsheet installed, and it hasn’t updated, you can open it in the App Store and force your device to update it.
As noted above, a clear improvement to Callsheet would be for it to support multiple lists of pins. Many users asked for some variation of three lists:
The headline feature of 2025.2 is exactly this: Callsheet now allows for more than one list
of pinned items. Additionally, the Pinned Items
list can now be renamed, though it cannot
be deleted.
As I was designing this feature, I wanted it to be completely ignorable if multiple lists just aren’t a user’s cup of tea. Furthermore, in order to keep the user interface straightforward, I wanted to avoid asking users “which list are you talking about?” every time a pin is added or removed. However, I wanted to make it possible for power users to quickly add or remove an item from any of their lists.
Callsheet now has a concept of an Active List, which is to say, the default list that pins will be shown, added to, or removed from. If you open an item in the app, and you tap the outline of the pin in the toolbar, the Active List is the list that will have a new pin in it. (This is the same way things have always worked in Callsheet; except that there could only ever be one list.)
However, if you tap and hold on the pin icon in the toolbar, you’ll be presented with a new pop-up menu:
Right now, this menu isn’t very interesting, as it only has the default Pinned Items
list in it.
Since Pinned Items
is the active (and, as it turns out, only) list, if I were to tap on that menu
item, it does the same thing as tapping the pin icon. Hunt would be added to the active list,
which is Pinned Items
.
Once you add a few lists, that menu gets more interesting:
If I were to select Have Watched
, then The Hunt for Red October would be added to that list.
This would be shown as a ✔️ on that menu item.
Now that Hunt is in the Have Watched
list, to indicate that this item is pinned
on an inactive list, the pin icon is striped:
To switch the Active List, return to the Discover
screen (the first screen
you see when the app opens). On the right-hand side, you’ll see a link/button
that either reads Add list…
(if you only have one list) or Switch list
buttons. Tapping Switch list
will change what the Active List is. Additionally,
you can see the items in the Active List by tapping the list name, just like
Callsheet has always worked.
I’ve also begun adding App Intents to the app, which in non-nerd means “you start to see Callsheet showing up in more places within your phone now”. This includes Spotlight:
As well as Shortcuts:
And Control Center:
An extremely common — and fair! — complaint about Callsheet is the way I handle long-running series. Take, for example, Sabrina Carpenter, who was born in 1999. She has made appearances on shows like Saturday Night Live, but, well, after 1999. Due to a quirk in the way TMDB works, the way the information about her appearance on SNL is returned to me is based on the year that Saturday Night Live first aired. Which was 1975. Almost 25 years before Sabrina was born.
Coming up with a fix for this quirk is more challenging than you’d think — to properly fix it would require making a lot of requests to the TMDB servers. While each request is very fast, in aggregate, it would make Callsheet seem far slower. I don’t think perfect accuracy in a person’s filmography is worth slowing down the app.
Instead, I’ve tried to thread the needle by doing my best to group shows like these
behind a new, collapsed, Long-Running Series
section on a person’s filmography. Here
you can see Sabrina’s filmography in version 2025.2; this is after I expanded the
Long-Running Series
section:
Note this is one of the first sections on a person’s filmography; it is slotted
between Upcoming
releases and the current year’s releases.
Good news: iPads with keyboards attached, as well as Macs, can now hit ⌘F to focus the search field.
Bad news: It’s a disgusting hack, and there’s nothing I can do about it. If anyone at Apple is reading this, FB16385764.
For the OG icon set — the clapperboard with magnifying glass — those icons now know what their dark variants are. For users that switch between light and dark mode, the OG icon set will automatically switch between light and dark variants.
Apologies for this taking so long, but to be completely honest, I hate the way my home screen looks with dark icons, so this wasn’t a high priority.
There’s also a handful of other changes in this release:
Discover
(main) screenKnown For
section when viewing a person.Briefly, there was a 2025.1, but early users reported a critical issue
with it, so I stopped the roll-out pretty quickly. If you’re one of the
[un]lucky few who got it, you may have seen two copies of the Pinned Items
list on your device. You may have also noticed that neither of them could
be deleted. Whoops. 🤦🏻♂️
2025.2 [hopefully] prevents this from happening, and provides an automated fix if it has happened. ↩
Ten years ago today, I wrote this post.
As many of my posts do, it used many words to say one simple thing:
In the end, all I can say is that I’m so deeply, deeply lucky to have found you, Erin.
Twenty years ago today, Erin and I decided to become boyfriend and girlfriend. I would tell you that I asked her; she would tell you she asked me. Given my horrible memory, she’s surely right, but I kinda like that a definitive answer is lost to time. I’ve retconned it to be that we just made the decision together.
Together. Which we’ve been — as of today — for twenty years.
Erin said to me recently that people are often a part of a season of your life. A great example of this is coworkers; you may be close while you work together, but once one of you leaves, and you’re no longer speaking every weekday, friendship often tends to leave as well.
It’s a different season.
To make it twenty years with anyone — even family — is an immense accomplishment. It requires choosing each other often. In a romantic relationship, it requires choosing each other every time. Even if you don’t particularly want to, at that particular moment. When it’s easy, but also when it’s hard. Especially when it is hard.
What’s been so great about spending twenty years with Erin is that it is very rarely hard. Well, for me anyway. 🫣
Our twenty year wedding anniversary isn’t until a couple summers from now. To many couples, that is the anniversary that matters most. To me, while our wedding was immensely important, it was a formal codification of something we already knew. Something we had known for two and a half years: we are each other’s person.
We’ve known that since 2005. Since the sixteenth of January, to be exact.
Here’s to twenty years, Erin. To merging two lives into one. To bringing two all-new lives into the world. To trying, every day. To the [overwhelming majority of] times where it doesn’t feel like trying at all. To choosing each other… every day.
I love you. Happy anniversary. 🥰
For years, my iPhone’s home screens looked about like this:
The first screen was the one I used most times. It featured a Carrot Weather widget at the top, four rows of icons, and the dock. Maybe ⅔ of the time I used my phone, I was able to do so using this screen. I worked very hard to curate this screen.
The second screen was 80% or so of the remaining ⅓ of the time I used my phone. It was mostly curated — I didn’t worry about it quite as much as I did the first screen, but I did definitely rearrange things from time to time.
The third screen was a junk drawer, and it looked like it.
Over time, I got really sick of this. Muscle memory let me get to most apps on the second and third screens quickly, but it just felt… gross. There must be a better way.
I’m pretty sure it was either in chatting with Myke, or listening to one of his shows, that I heard him discuss a different approach. Though he’s not the only one to espouse working with your iPhone this way. I had long found this technique to be… well… lunacy. But around fall of last year, the idea of it started to really pull at me.
That approach — that new home screen thought technology — was Spotlight, and the App Library.
Myke discussed how he often uses Spotlight to get to his apps. Said differently, he searches for them instead of swiping around and tapping on the icon on his home screen. I knew of others who did the same thing, and it always seemed so inefficient and bonkers to me.
But the idea kept eating at me.
The more I thought about it, the more I felt like most of the time I used my iPhone really was concentrated on a handful of apps. The rest of the time could be any of the literally of hundreds of other apps I had on my phone. With my then-current approach of every-app-must-live-on-a-home-screen, I was optimizing a ton for apps I rarely needed.
So I ran an experiment.
I decided to rejigger my home screens and take a wildly new [to me] approach.
Here’s what I came up with:
The first home screen looks pretty much the same. There are some minor changes — the images of the old home screens are actually from late summer 2022. However, in broad strokes, it’s the same.
The second home screen is wildly different. There are three slots for widgets:
On This Day
Featured
photos widgetEvent List + Calendar
widgetYour Deliveries
widgetGoals Summary
widget.Also on the second screen are four frequently used apps that couldn’t quite make it to my first screen:
There is no third home screen.
The key to this approach is to embrace Spotlight and — to a lesser extent — the App Library.
I didn’t think I’d be able to last this way. I never cared for Spotlight, and almost never opened the App Library. I generally found them both burdensome.
After a couple of days, I was entirely adjusted. I’m here to tell you that if I can make it work for me, you can make it work for you, too.
I started with this as an experiment last fall, I’ve come to absolutely love this approach. I’ve optimized my home screens for 80% usage; about 80% of the time, I’m opening one of the apps on one of these two home screens. For the rest of the time, I have Spotlight and the App Library.
Not only has this sped up opening the app I want, it’s also cut down on the number of badges I see. While I know I could control that in Settings, it’s a far easier solution to make them just… disappear.
This new home screen thought technology took a long time to really sink in, but once I embraced it, I almost immediately fell in love.
For more on my rationale and journey, you can hear Myke and I also discuss this on Analog(ue) episode #230.
]]>]]>I had been describing myself as disgusted by this result, but I eventually realized that was incorrect. What I actually am is disillusioned. America was a dream, and now that dream is gone from me. Holding on to beliefs like “good triumphs over evil” and “justice will be served” has always required taking a long view. But now, when a convicted felon has yet again managed to evade consequences and scam his way into the presidency, I find my faith in my country shattered. It lies in pieces at my feet and I am unsure what I’m going to do about that.
Callsheet — my app that’s like IMDB but for people with taste — uses iCloud for several things, notably, storing your “pinned items”. Internally, I call pinned items “favorites”, so in this post I’ll refer to them interchangably. Regardless, today, there’s only one list of pinned items.
My most-requested feature is to add support for multiple lists. Generally, so people can have things like To Watch, Watching, and Did Watch. Naturally, your particular needs may differ, but this request is common… and constant.
Due mostly to life obligations (all is well), but also my fear of how much work this will be, I’ve been kicking this can down the road for a while now. (See also: compliance to Swift 6’s strict concurrency). This week, I’ve started to really dig in.
Today I hit a wall, and I’m too tired and exasperated to do a long and involved write-up with pictures and stuff. The short-short version is:
Make sure you add indexes before adding data that you need to query that relies on those indexes; if you don’t, they may not work as expected.
When tackling multiple lists of pins, the first thing I did was to add a
new record type (“table”, sorta-kinda) called FavoriteList
, which is peer
to the pre-existing Favorite
. Then I added a new field (“column”,
sorta-kinda) to Favorite
that contains a CKRecord.Reference
to the favorite/pin’s parent list.
Once I got the schema updated in development, I started writing code. Thanks
to some genuinely (and uncharacteristically for Apple) helpful sample code,
I was able to put something together quickly. However, it didn’t work. Thanks
to some genuinely (and uncharacteristically for Apple) helpful error messaging,
I quickly realized I needed to add an index to Favorite
; specifically for
this new reference field I added.
I added the compulsory indexes, and then went back to writing code.
Quickly, I was flummoxed; things still weren’t working. Now, for a different
reason: in trying to get favorites that are part of a given list, I was coming
up short. No matter what I tried, no records were being returned. When I looked
at the records in the iCloud Developer Dashboard, it appeared everything was
good. I could click from the field in Favorite
and it would load the list
in FavoriteList
. But whenever I tried to query using my code, it wouldn’t
work. I would come up with no records.
In chatting with some incredibly kind folks on Slack, one of them asked me to try to replicate the query I was doing in code on the Developer Dashboard. I got the same empty results. This was a critical tip, because it led me to believe that my own code was not — for once — the issue.
After a couple hours of this, and in desperation, I deleted all my existing
Favorite
and added new ones. I tried my queries again, and they worked no
sweat. Both on the dashboard and in my code.
Thinking about this some more, and in chatting with the good samaritans on Slack, we all came to the same conclusion: the index probably didn’t properly… uh… index… whatever data was already there before the index was created.
So, if you’re ever in a situation where you’re getting wonky results (or no results at all) from a new field in CloudKit, consider whether the data you’ve added was done so before or after you added the corresponding index.
Apple folks, FB15563372.
]]>Since we last spoke about Tailscale, my post was linked by Tailscale themselves, and I’m pleased to report Tailscale has sponsored one of my podcasts a couple times.
With that in mind, I want to make it clear that they do not know I’m writing this post, and have not requested anything of me with regard to my website.
I just cleanly solved a weird problem that’s been nagging me for a very long time, and thanks to Tailscale, I was able to do so quite easily. I thought it was a great case study in what makes Tailscale so amazing.
My parents have a network attached storage that they use for, among other things, recording security cameras locally. As such, they will occasionally access it remotely, and thus having a SSL certificate is pretty much required these days.
Thankfully, Synology makes it easy to request a Let’s Encrypt certificate. However, to do so requires you to have the router forward ports 80 and 443 to the Synology. Generally speaking, I do not want those ports forwarded anywhere, so I only turn on those forwards when renewal time comes around.
Messing about with their in-home router is not easily accomplished from my house, 45 minutes away from theirs. Furthermore, Let’s Encrypt certificates have to be refreshed every 90 days, so this dance happens quarterly.
My predicament is complicated by my parents’ oddball ISP-issued router refusing to respond to network requests coming from outside the network. Even over a traditional VPN hosted in their house, the router would not respond to requests from my house. This very well could have been user error, but that was what I experienced.
I needed a way to have packets from my computer appear to be originating on my parents’ network.
My first solution to this was to host a docker container that basically exposed Firefox via the web. Yo dogg, I heard you like web browsers in your web browsers. So, the process was:
http://192.168.1.x
This worked just fine, but it was… involved.
Tailscale is many many different features, all rolled into one product. A core tenet of Tailscale is that you should be able to migrate to Tailscale incrementally; you shouldn’t have to go all-in from the get-go. Tailscale has some features that help facilitate incremental adoption.
One of these features is Subnet routers; they are summarized quite well in the Tailscale documentation:
In cases [where you can’t install Tailscale on every device], you can set up a subnet router to access these devices from your Tailscale network (known as a tailnet). Subnet routers act as a gateway, relaying traffic from your tailnet to a physical subnet.
In short, if you have a subnet router, you can route
local network → tailnet → subnet router → remote network
So, in principle, I could enter my parents’ router’s IP address into my browser, and it will load.
The way subnet routers typically work is that they effectively bridge two networks
together. My parents’ network is 192.168.1.x
; mine is 192.168.17.x
. On the
surface, this seems fine, but so many networks are 192.168.1.x
. I suspect
there will come a time one of my portable devices is on a 192.168.1.x
network,
and I may want to reach local devices on that network. By default, a basic
subnet router will intercept all requests to 192.168.1.x
and try to serve them
via my parents’ network. That could easily make things wonky, and lead to
my devices not being able to reach local peers.
I’d like my parents’ network to stay… at my parents’ house. I’d just like to be able to peek into it for the purposes of tweaking their router’s settings every now and again.
Tailscale has another trick up its sleeve that I realized would be a perfect fit for this scenario. Tailscale offers “4via6” subnet routers. It occurred to me recently that this is the fix I’ve been looking for.
Tailscale’s problem statement is this:
In a large network, you may have existing subnets with overlapping IPv4 addresses. If there are two entirely separate virtual private clouds (VPCs) using the identical set of IPs […]
That’s the scenario I’m worried about: I’m on a 192.168.1.x
network, but my
parents have already “claimed” the 192.168.1.x
address space.
4via6 routers solve this by:
The 4via6 (“4 via 6”) subnet router feature provides an unambiguous, unique IPv6 address for each overlapping subnet, so a Tailscale node’s traffic is routed to the correct device.
In short, I told the subnet router at my parents’ “I’d like you to expose this [otherwise internal] network on the tailnet only as IPv6”. That means all the IPv4 addresses in my parents’ house are exposed using a special IPv6 address:
fd7a:115c:a1e0:b1a:0:XXXX:YYYY:YYYY
Where:
fd7a:115c:a1e0:b1a:0
= a special Tailscale prefix to indicate 4via6XXXX
= the identifier for the target subnetYYYY
= the IPv4 address represented in hexSo, if I’ve designated my parents’ house as 123
(which is 7b
in hex), then
192.168.1.1
would be:
fd7a:115c:a1e0:b1a:0:7b:c0a8:101
Progress! However, an IPv6 address is not particularly memorable, which kinda stinks.
Tailscale also offers “MagicDNS”, which does many things, but it will also convert/resolve specially formed hostnames into the appropriate IPv6 addresses.
So, if my tailnet name is smiley-tiger.ts.net
, then I can open my parents’
router’s configuration page by entering this URL into a browser on my computer:
http://192-168-1-1-via-123.smiley-tiger.ts.net/
That will automatically get resolved by MagicDNS to
http://fd7a:115c:a1e0:b1a:0:7b:c0a8:101
…which in turn lets me log into my parents’ router remotely, anytime, without any fiddling nor Docker containers required.
I can do this no matter what network my computer is on, as long as my subnet router at my parents’ is also on. Tailscale connects everything together.
This is what makes Tailscale so great — once you can connect your devices together, it opens a world of possibilities. Those possibilities easily extend to the devices on your tailnet, but they can extend to devices beyond your tailnet, with just a little bit of work.
Seriously, Tailscale is so great; you really should try it.
At some point, I should probably look into automating some (all?) of this certificate renewal process, but for right now, I’m happy to bask in the work being far quicker and easier than it’s ever been before.
]]>