Home / Blog / Engineering

Engineering

How the daily story gets written

Tap once and PicLoom turns "what's in these photos, where they were taken, and what the weather was" into a short story. This post unpacks how it works and where each privacy boundary is drawn.

Step one: collect three kinds of facts

The raw material of a story is structured facts, not the photos themselves. Before writing anything about a day, the engine gathers three things:

DayFactsCollector
  ├─ Vision labels       (on device, no network)
  ├─ CLGeocoder place    (cached per ~1 km)
  └─ Open-Meteo weather  (rounded coords + date, cached per day)

Step two: three engines, one contract

With facts in hand, the selected "story engine" turns them into text. All three engines honor the same output contract, so you can switch anytime:

The cloud engine's default boundary

Cloud generation is optional and off by default: only structured text facts are sent (date, weather, place name, content labels) — no photos. Attaching compressed photos is a separate switch buried in settings, off by default, with an explicit confirmation before it can be enabled.

The privacy model in one sentence: analysis on device, weather sends only coordinates and a date, the cloud sends only text by default, and photos are always opt-in.

Caching and offline use

Opening a saved story reads its local copy. Regeneration may collect fresh facts and, when selected, make a new cloud request. A failed save retains the generated text for retry without another generation. The previous valid saved version is kept for recovery. Widget previews use the shared snapshot; its keep action also verifies StoreKit access.

Why the default engine is on-device

Baking the best out-of-box experience onto the device model means users without an API key still get natural, well-written stories, with the strongest privacy setting as the default. The template engine guarantees the feature always works, and the cloud engine is there for people who want to configure their own — three tiers of graceful degradation that leave nobody out.

Implementation details will keep evolving; the behavior contract lives in spec/001-day-story.md in the repo. Next post: widget design notes: five moments a day.