Feed format¶
Stable within a major version. Both readers (Kotlin and Swift) implement exactly these rules.
<root>/<directoryName>/
current.json {"current":"<id>","previous":["<id>", ...]}
generations/<id>/
feed.json {"schemaVersion":<int>,"generatedAtEpochMs":<long>,"fingerprint":"<sha256 hex>","payload":{...}}
assets/<fileName>
generations/<id>.tmp/ in-progress write, ignored by readers
<root>: Androidcontext.filesDir; iOS the App Group container.<id>:<generatedAtEpochMs>-<first 8 hex of fingerprint>.fingerprint: sha256 over the schema version, the canonical (compact) payload JSON, and each asset's name, size and content hash, sorted by name.publishskips writing when it matches the current generation.- Safe names (ids, asset file names): letters, digits,
-,_,.; never blank. Everything else is refused on write and skipped on read.
Reader rules¶
- Candidate order:
current, thenpreviousin order, then every other non-.tmpgeneration directory sorted descending by id. Duplicates removed. - A candidate is accepted when
feed.jsonparses andschemaVersionequals the reader's. assetPath/assetURLreturn a value only when the name is safe and the resolved path stays undergenerations/<id>/and the file exists.- Readers never throw for corrupt or foreign files; they return the next candidate or nothing.
Writer rules¶
- Write
generations/<id>.tmp/assets/*, thenfeed.json, then rename the folder togenerations/<id>/. - Write
current.jsonatomically (temp + rename on Android,writeToFile(atomically:)on iOS). - Delete every generation not referenced by the new pointer (
keepPreviousold ones are kept). - Ask the OS to redraw.