I recently released a tiny new Mac utility, Three Month Calendar. It’s a minimal calendar app, showing three months (fifteen weeks, actually) at a time, in a scrolling, compact view. It runs as a menu bar item, floats on top of other windows, has a proper dark mode and a few other appearance settings, and doesn’t take keyboard focus. It’s for when you want to glance at a calendar, and more than just one month; maybe you want to know what day it is three Mondays from now, for example. Learn more on that webpage, or check it out on the App Store (99¢).
I built this app to have it; it’s running on my desktop now. I also built it to build it, as a small, focused, legacy-free app, and a break between major TimeStory releases.
A historical aside
And it was a fun personal throwback. I’d picked up iPhone development in my spare time as soon as the SDK was released, and in 2009, I built an app called “Three Month Calendar”. It was basically this, but as an iPhone app. I never published it, but I still have a copy of the code, in all its Objective-C manual-retain/release glory.
The SwiftUI choice
In 2021, SwiftUI is clearly the ticket for an app like this, with one main window, one preferences window, and a layout which can easily be described in terms of vertical and horizontal stacks.
It offers a fast, well-designed, expressive, and pleasant developer experience, but with tradeoffs to understand. (I’m not talking about bugs or missing features, but about the tradeoffs intrinsic in its design.) Here are some examples from this little project.
- A cross-platform, declarative framework will always have compromises.
For example, consider window management. I started out using SwiftUI’s
App
andWindowGroup
. But it turns out that there’s no way to start up the app with no windows open, or to open a nonactivating panel; perfectly normal on macOS, but nonsense on iOS. So I ditchedApp
, but then realized that this meant I couldn’t useSettings
, SwiftUI’s built-in support for preferences windows which look good on both Mac and iOS. So I ended up with a bit of a hybrid. - When things don’t work, or when they perform poorly, it’s sometimes more difficult to debug. Your code doesn’t have any real connection to the code doing things on screen. AppKit may be closed source and opaque, but it’s also breakpointable, steppable, and, in the profiler, stack traces tend to lead from its code to yours.
- SwiftUI is in flux, and it uses Swift features which, themselves, are in flux, so you need to budget a bit more for testing and churn. After installing 3MC on my brand-new MacBook Pro running Monterey, I immediately saw two very visible problems in the UI which hadn’t been there on my Big Sur Intel Mac. One was a bug in my code, revealed by a new fix in SwiftUI; the other was a bug in SwiftUI itself.
The question of when to use SwiftUI is thus an interesting one, not a simple one. At its best, it is vastly nicer to use than AppKit, and saves a lot of time while encouraging great app UIs. It was a great fit in this case. But I don’t plan on investing much in using it in TimeStory just yet (even for the new iOS port). I’ll be looking for small bits of UI where I can use it. But TimeStory uses a fair bit of platform-specific and customized behaviors; it has a large, custom document layout engine which I need to know how to troubleshoot and profile; and it’s big enough that minor UI breakage on one of the OS versions I support might go unnoticed for longer than I like. (And I’m one person; if I had a small team of testers and designers and automators, that would change the equation.)