Today I have three updates for you: a milestone in FoodNoms' growth, recent database improvements, and a deep-dive into some of the infrastructural improvements with FoodNoms 2.
FoodNoms just passed $4K MRR! I'm super proud of this milestone and very happy to see FoodNoms continue to grow. I'm feeding it all back into the business to improve the product and help with marketing. Thanks again to FoodNoms Plus subscribers for believing in me and supporting the app's development. 🧡
Last week I pushed up some major improvements to the FoodNoms database:
Since this update, barcode success rate has increased globally >2% (67% to 71%) and the number of searches without any results has decreased by around 1% (13.5% to 12.5%). These deltas may not sound like much, but I'm actually quite happy with these numbers. It's getting harder to move these metrics as time marches on, and any significant improvement in the right direction is a win.
In addition to the new restaurants added, I now have a proper system to bulk-import restaurant foods from CSV files. This will make it much easier to add more restaurant foods going forward.
The community database continues to grow and deliver better results every month. In the US, barcode scan success rate is up to 83.25% - that's HUGE compared to just 24.5% 18 months ago! Outside the US, barcode scan success rate still has a long ways to go. But it's currently at 35%, which is way higher than just 3% 18 months ago!
To date, I've mostly focused on the barcode search success rate and increasing the quantity of foods in the database. My next focus area for the database will be on the search algorithm – including resilience to typos and improved item rankings.
FoodNoms 2 is coming along nicely. Lately I've been working on some highly-requested features related to goals (stay tuned for more info) and wrapping up some key infrastructural projects.
"Robust". I can't think of a better word to describe my aspirations for FoodNoms 2.
There's a lot of things that can go wrong with bidirectional HealthKit and CloudKit syncing. I have architected FoodNoms 2 to be resilient to crashes, sudden terminations, errors, conflicting data, etc.
How am I making syncing robust? First, I've thoughtful modeled user data to gracefully handle conflicts. My general strategy is to avoid database-level constraints, always using UUIDs for primary keys, having a consistent tie-breaker strategy, and avoiding de-duplicating or any sort of update that can cascade or infinitely loop.
The second major component is a persistent job queue. The persistent queue keeps track of items to be processed. If for any reason the app is terminated or disrupted, it won't lose track of any tasks it was working on.
Logging, testing, user-visible diagnostics, and error handling is critical to completing a robust syncing experience.
Charts in FoodNoms 1 are drawn with SwiftUI H/V/ZStacks, Shapes, and Paths. One thing I learned from building these charts is that it's not a good idea to depend on natural flow positioning from HStacks/VStacks in data visualizations. It's much better to completely control the coordinates and sizing of everything that gets drawn, because it's easier to debug, test, and get things aligned perfectly.
Having previous experience with D3, I realized what I really needed is a swift-equivalent of D3 scale. D3 scale is the key part of D3 that deals with one of the hardest parts of making data visualizations – just figuring out where everything should go. So I literally ported it over to Swift and am using it to help draw all of the charts in FoodNoms 2 (in UIKit, but it would work completely fine in SwiftUI too).
So far, I've only used it recreate the goal bar charts that are displayed in the app today, but I'm looking forward to using it more in the future.
I've never been happy with how I've implemented navigation in UIKit apps. Ad-hoc pushing and presenting view controllers always felt brittle, and I always ended up with multiple code paths to presenting the same view controller.
Meanwhile, for 5+ years now I have been using react-router at my day job. It's by far, my favorite way to handle navigation. Inspired by react-router, I've built my own routing protocol that enables a declarative, path-driven navigation API. This has made it possible to add features like handoff and state restoration without having to do any extra work. Not to mention being able to reuse these paths for widget links and add support for a public URL API that can be used with Shortcuts.
That's all for now. If you want to make sure you don't miss out on any future updates, subscribe to the new FoodNoms Insiders newsletter (no spam, I promise!).
– Ryan (@ryanashcraft)