2019 was the year I switched to full-time independent software development and built TimeStory, my Mac app, with the help of my wife Hemi. I thought it would be fun to write a bit about this experience as the year comes to a close.
(Hemi also wrote her own 2019 reflection; there’s more context there about this, about her own job change this year, and more.)
The core idea behind TimeStory, for me, goes back to the several years I spent as an engineering manager starting in 2009. I was given a chance to switch to this management role during a time when we had a lot of interesting product work, a lot of remote people to work with, and a changing business environment. In those days, I often used Excel to map out upcoming work on a date grid, as people often do. This gets cumbersome to edit, and difficult to extract data from.
At one point, I fired up Xcode and prototyped a simple timeline sketching app for iOS. I didn’t get very far, and abandoned it. But the idea felt sound, and persisted. I wanted an app for sketching timelines. On the one hand, it shouldn’t just be a plain grid of cells or canvas of shapes; it should have a data model reflecting events in time. On the other hand, I didn’t care about having it keep me honest or help me with detailed project planning; I left that to the project managers.
Fast forward to 2018. Hemi was responsible for a large team which was building software across multiple global markets in parallel, and had many moving parts to keep track of. She was trying out various timeline-authoring tools, but hadn’t found one which checked off all of her boxes: among other things, something she could leave open all the time, was fast and responsive even with large numbers of events, and which offered quick entry of data.
So we started talking about building something new. I was enjoying my consulting gig at this time, working with great people on some interesting problems, but it was basically a coding role on an existing system. This would be a chance to start from scratch and craft something new that I knew people could use, and it really sounded exciting. On top of that, I knew this was a great chance to build a native Mac app, something new to me. I had shipped some iOS and Apple Watch code, and had delivered Java code on the Mac, but never anything native.
Then, Hemi found a great new job. Our second and youngest kid would be heading to college in 2019. Everything felt ready. So we made the decision, and I let the contract end in December.
I started right after the winter holidays. I blocked out a couple of weeks to dig in, figure out what I needed to learn, and make plans.
My primary tool at this point was Workflowy; I’ve never treated it as a task manager, just as a great, smooth, outlining tool. My outline grew to many branches, including everything from UX flows, to links to other apps, to a basic architecture, to the core data model, to proposed names for the product. There was a lot of iteration and backtracking, which is cheap when you’re working by yourself and don’t risk wasting other people’s time.
I specified a data model and basic document format, and hand-created the first few documents.
Then I fired up Xcode. I created the data model types, the file I/O logic, and a bit more, and wrapped it up in a purely textual command-line interface. I wrapped that CLI behind some test scripts (in Bash) which validated file reading, file writing, and preservation of data between the two, and I was off to the races! I proceeded to write document layout logic and even Quartz-based document rendering, extending the CLI to be able to spit out PNG renderings of document files, before ever having a line of GUI code.
Each of these areas would be incrementally changed, refactored, or even partially rewritten throughout the year, but never underestimate the value of having something that works at all times to keep yourself moving and your design sense clear. And especially never underestimate the value of adopting automated tests early in your development cycle—they transform design assertions into source-controlled code. (Even if you don’t have CI in place, and have to run them manually.)
Integration tests, in particular, are very powerful. If you have an app where you can run a lot of end-to-end use cases without any GUI involved, and make stable assertions at the level of program inputs and outputs, you can quickly build a lot of valuable test collateral that survives GUI rework and even internal API refactorings. Those tests caught many mistakes over the year, have grown to include a suite of both good and malformed document files, and were the best investment I’ve made. After all, if I ship a bug in the UI, I have to push a patch, but if I ship a bug in the file-saving code, you lose data.
It wasn’t till February that I created a Mac GUI app target and started
writing AppKit code. This was mainly an exercise in learning AppKit and
wrapping up my existing core, which worked pretty well. The document object in
NSDocument, the document rendering code in an
NSView subclass, and so
on. I became initiated into the mysteries of
I learned to supply
NSToolbar with items, I puzzled over whether
was really deprecated, and so on.
And I like it. I actually think I like AppKit better than UIKit, despite all its legacy and complexity, though I’m not sure I could tell you why. Maybe because of the legacy—sometimes I just enjoy the feel of an API with real history to it. People have solved a lot of problems with that code.
(After 1.0, I also added a simple iOS (UIKit) target to my build, wrapping the same core logic in a minimal UI. I’ll talk more about my plans for that below, but I felt it was important to add that to my process so I could avoid adding non-portable logic to the core layer.)
We didn’t settle on the name until June. Throughout the whole process, I maintained an outline in Workflowy with proposed names we’d brainstormed, cross-checking domain names, Twitter handles, Google search results, Mac and iOS app store searches, and more.
“TimeStory” was Hemi’s invention. I came to love it. It evokes storytelling; it is a clever inversion of “story time”; it had an .app domain available; it’s easy to remember.
The App Store
I have no experience in directly selling software from my own site. I want to get there. But I’m not there yet. The Mac App Store’s 30% tax isn’t great, but for someone like me, the handling of worldwide payments, taxes, and sales support is worth a lot, and the discoverability (as noted below) is actually better than I’d expected.
Release and Sales
I released it in July, and got my first sale on day 1, from someone who found it via App Store search. Since then, there’s been a pretty steady rate of customers who found it that way. My keywords are obvious, and I don’t even know how to spell ASO, so there’s obviously at least some demand.
And I started getting feedback.
I got my first negative feedback, a pretty angry email, early on. But this customer had run into real problems.
I do most of my work on my MacBook and tend to use the built-in trackpad for everything. At that point, I had done too little testing with an external mouse; on macOS, mice cause the default transparent overlay scrollers to become actual persistent scroll bars, and my code didn’t handle that right, causing misaligned and cut off layouts in some cases. More, I relied on gestures for navigation that are not obvious for non-trackpad users. It’s actually a bit embarrassing to write that I neglected mouse support on a Mac, but there you have it. The fixes were quick and, as such fixes usually do, led to a better product for everyone, as I improved the navigation controls and layout code overall.
I was, and remain, very grateful that this user took the time to write and tell me that my app looked incomplete and unprofessional rather than just delete it and one-star me in the App Store.
I also got happy emails and reviews. My first five-star review (alas, not on the US app store) led to an email follow-up thread, some great feature requests, a better product, and a tremendous personal boost. I’m actually surprised how many positive email messages I’ve gotten, often with feature requests or concrete feedback. It’s the best part of the work. It’s made me think more about reaching out to the developers of the apps I love.
And I iterated. My goal this year was really a “soft launch”: launch with a good, useful, but simple product, and iterate as I accumulate user input, use cases, and Mac development expertise. I think I hit that target: the December version of TimeStory is vastly better than the July version was, my ticket backlog is full of great things that I know I can ship, and my confidence is high.
Side note: WWDC2019
Just as I was stabilizing for 1.0, WWDC happened. I had planned it as a low-coding week, so I could keep up with videos and blogs as they appeared, and boy did Apple deliver.
I had, of course, followed the “Marzipan” rumors all along, and Catalyst was pretty much as expected. It’s very cool, but it’s not the right tool for TimeStory, for reasons hinted at earlier. I want a standard, document-based Mac app, which just looks and feels very different from a document-based iOS app. Pragmatically, I do a lot of custom mouse and keyboard handling, which sounds possible but not necessarily easy in Catalyst. And, personally, any “cross-platform” solution always has a high bar to clear for me, just because, as a long-time Java developer, I know how much work there is in that last 20%.
SwiftUI took me by surprise. I’d missed any hint of those rumors. It looks like a fantastic start. I’ve started playing with it on the side; for smaller, more focused apps, it’s exciting. I love that they announced it on day one with bidirectional integration with UIKit and AppKit, so we can adopt it one view controller at a time. I don’t love how quickly I can crash the preview or how hard it is to implement basic macOS or iOS behaviors sometimes, but those things will get fixed.
(Coding in SwiftUI also really brings back early 2000’s-era C++ to me, when many new template-based libraries were coming into use, faster than the compilers could improve their diagnostics. One typo could result in many pages of errors, sometimes pointing to lines unrelated to the error. But that will get fixed too.)
So, what’s next?
I need to start marketing more.
I want to deliver an iOS version or two. My plan has been to start with a read-only “TimeStory Viewer”, to help users who want to review or present a timeline, and as a first technical step towards a full-parity version (at least on the iPad). I’ve got working but incomplete iOS code now, but a good UX still needs a lot of work. It will also slow down future releases; once it’s out, I need same-day release of iOS and macOS apps, or people will use new features on one, and find their documents stranded, unable to be opened on the other.
There will be a 2.0. I’ve got a big list of features planned. Some of them will be transformative, I think; they will open the product to many new users and applications. The foundation is solid, and I know how to get them shipped.
Finally, TimeStory was designed, from day one, to be part of a larger system of software. I wanted it to be scriptable, to have solid importers and exporters, and to have its file format and data model in a form that I could reuse in multiple apps. 2020 is the year that I hope this starts to pay off. I hope to publish extensions and examples that tie it in to journaling, time-oriented data visualization, and other domains.
There’s a lot to do. I probably won’t get everything I want in 2020, but I like where I am, and I feel good about what’s to come.