Last time I wrote about moving Aoede's reader onto TextKit 2, and the version I described rendered the whole book as one long attributed string. It looked clean and it was fine for a short document, but it did not scale. A long book meant laying the entire thing out before I could show the first page, and on iOS that was slow and heavy enough to feel broken. The structural bet from last time was half right. TextKit was the correct tool, but rendering the book as a single object was the wrong shape.
What surprised me was how hard it was to get from there to the right shape. When I asked the AI doing the work to fix the scaling, it kept reaching for architectures that did not hold up. It would rebuild the single whole-document attributed string a different way, or quietly pull the old SwiftUI reader components back into the TextKit path, which dragged the per-word-view cost right back in with them. It could execute almost any approach cleanly. It just kept choosing ones that fell over on a real 400-page book. The unlock was an architectural call I had to make and then insist on: stop rendering the book as one thing. Put it in a list, one cell per paragraph, each cell laid out with TextKit. On iOS that is a UICollectionView, on macOS an NSCollectionView with a custom layout for per-paragraph heights. The list only realizes the paragraphs near the screen, so a long book opens as fast as a short one because it never lays out more than a screenful at a time.
Resume took its own pass to get right. To reopen a book exactly where I left off, down to the current word inside a long paragraph, the list has to know each cell's height before it scrolls there, so I size the items up front instead of letting them self-size lazily as they appear. Image-heavy books needed a little more, since the height of a picture is not known until it loads, so the resume re-asserts its target until the layout settles. The cell-list reader is now the default on both platforms, the whole-document version is gone, and the original SwiftUI reader stays behind a Labs toggle for comparison. Long books finally load fast, including on iPhone and iPad, and they come back to the exact spot I stopped. The lesson I keep relearning is that the model is very good at building an architecture and not always at picking the one that survives contact with a real book. That part still needed me.