Introducing Leaf
Leaf — Dev Log #1: Sync Works
For the past few months, I’ve been working on a new app.
I wasn’t planning to. I already maintain four other apps and have a full-time job as an electrical engineer. That feels like enough most days. But this idea has been sitting in the back of my head for years, and I finally gave in.
So I’m building Leaf, an EPUB reader for the kind of person I am.
Here’s Leaf, as it exists today:

Leaf syncs your library and reading progress across iOS, iPadOS, and macOS using CloudKit. It will be subscription-based. The goal is to keep it sustainable long term without turning it into something noisy or bloated.
This post kicks off a dev log series. It’s mostly for me and future me. I want a record of what I was thinking, what I broke, and what changed as I built this.
This week felt like a real milestone.
Sync works.
Leaf is for a specific kind of reader.
The one with a carefully organized Calibre or BookLore library. The one who reads on an e-ink device but occasionally switches to their phone. The one who’s tired of “sync” meaning “usually.”
That’s me.
What I wanted didn’t really exist on iOS: reliable sync with KOReader on my e-ink device of choice and the ability to manually push and pull reading progress when I want to.
In Leaf:
- Leaf-to-Leaf sync is automatic.
- KOReader sync is manual and explicit.
- You can see timestamps and device origin.
- Conflicts are surfaced and you decide which one wins.
You know where you are in your book better than the app does.
Right now, syncing from KOReader lands you in the right general spot, usually within a page or so. Different fonts, margins, and screen sizes make perfect alignment unrealistic, but it’s close enough to start using.
The important part is that it behaves the way I expect it to.
I’m also experimenting with a small annual visual tracker. For each book finished between January 1 and December 31, a leaf drifts down from the top of the Home view. It resets each year. Maybe you’ll be able to look back at prior years. I’m still figuring that out.
It’s subtle. No streak pressure. Just a quiet record of the year.
My first app, Bound, is still my most successful. I’m proud of it.
But it was the first app I ever wrote. The architecture is rigid. The data model wasn’t designed around sync. And the most requested feature for years has been cross-device syncing.
For various reasons, that hasn’t happened yet. Legacy structure. Data model limitations. And I don’t trust Core Data’s sync story enough to build something this critical on top of it.
Leaf is me starting over with a sync-first data model. It’s also the first app where I’ve taken testing seriously from day one. That alone changes how I feel about touching the database.
CloudKit makes things permanent. Once a schema is live, it’s live. You don’t casually remove fields or restructure things without thinking about older versions of the app still out there.
That’s part of why I’m a little nervous. I want to be confident in the schema before I start syncing real reading data across devices.
But I’m close.
Now I need to actually use it.
The plan is to start dogfooding Leaf across my phone and KOReader and read a full book, not just a few pages while testing.
If it holds up, Leaf becomes my primary reader. If it breaks, I fix it.
Either way, it’s time to find out.