Challenges I ran into

1. iOS does not allow video encoding in the background.

Packing hundreds of photos takes minutes. Switch apps and VideoToolbox invalidates the session mid-write. The obvious fix, beginBackgroundTask, buys roughly 30 seconds of execution time — not the right to use the hardware encoder. No implementation finishes the job in the background.

So the goal changed from finishing to two things that are achievable: don't get interrupted (hold the idle timer while encoding — a sleeping screen is the most common cause) and don't break when interrupted (cancel on scenePhase == .background, delete the partial file, offer a resumable "it stopped part way" screen).

2. One placeholder bug, wrong in 14 languages at once.

The interruption screen read "Of 86 photos, 86 were packed" when the real count was 18. When a String Catalog substitution (%#@photos@) declares an explicit argNum, a following bare %lld reverts to automatic numbering and points back at argument 1. Five keys × 14 languages = 69 broken strings, none of them visible in Japanese, since the source language uses no substitutions.

Fixed by deriving the correct positional specifier from argument types, then added to the lint script so it cannot recur silently.

3. A storage app leaking storage.

35 orphaned .mov files — 521MB — on the test device, left by abandoned flows and interrupted encodes; later another 24MB in the temp directory from exports that were never saved. An app that promises to free space cannot be the thing consuming it. Both paths are swept at launch, under a rule that never touches a file written after the app started.

4. Saying "lossy" in 15 languages without softening it.

A pack does not preserve the original file, its metadata, Live Photo, or HDR. The words "lossless" and "same as the original" are banned from the copy. Every translation had to keep that edge — the risk in localization is not awkward phrasing, it is a translator making the promise sound safer than it is.

Accomplishments that I'm proud of

  • The three-choice rule held. Delete / Later / Keep as original, on every organize screen. Pushing toward deletion would convert better. It would also make the app something I would not install.
  • The master frame format. More work upfront than encoding the finished video directly, and the reason later features applied retroactively to existing packs.
  • 15 languages with machine-checked invariants rather than spot checks — untranslated keys, source-language leakage, plurals missing their source entry, and broken placeholders all fail make l10n-check.
  • 105 store screenshots regenerate from one command. Editing one headline re-renders every locale.

What I learned

  • A deletion feature is a decision problem, not a storage problem. The blocker was never "how do I free space" — it was that keep-or-delete has no correct answer. Adding a third option did more than any compression ratio.
  • Localization is correctness, not translation. The worst bug was not an awkward sentence; it was a placeholder printing the wrong number in 14 languages, silently, while the source language stayed correct.
  • "Just add beginBackgroundTask" is the kind of answer that sounds right and is wrong. Reading what the API actually guarantees turned a broken feature into a well-defined one.

What's next for DAYPACK

  • Measured encode times on device for 50 / 100 / 300 photos, and a resumable encode so a long pack survives leaving the app.
  • Accessibility pass — VoiceOver labels throughout, and honoring Reduce Motion for the squeeze and the confetti.
  • Clustering beyond capture interval. Visual similarity would make each pack tighter than time alone can.

Built With

Share this project:

Updates

Submission history