-
-
What if the sky chose your next page? Almanac turns live weather into a doorway to literature.
-
Today’s rain meets a page from 1922—matching the forecast with a passage written about the same kind of sky.
-
The demo shows seven skies; the app knows eight kinds of weather. Snow brings Joyce's Dubliners (1914).
-
Swipe back through previously discovered passages, like turning backward through a personal weather journal.
-
The current passage appears on iOS and Android home-screen widgets without opening the app.
-
One non-consumable purchase unlocks the complete shelf of 428 curated literary passages—no subscription.
-
One Kotlin Multiplatform core powers the Android, iOS, and desktop experiences, plus native home-screen widgets.
-
The live reading screen pairs the current weather with a hand-curated public-domain passage and its publication year.
-
Look up. Read back. Almanac is available on the App Store and Google Play.
-
App icon (1024 × 1024).
-
iPhone screenshot (1179 × 2556): a cloudy day and Virginia Woolf's Mrs Dalloway (1925).
Inspiration
Weather apps tell us what the sky is about to do. I wanted to make one that also tells us who else has stood under a sky like this.
104 years ago — someone watched this rain.
The Enchanted April, 1922 · Elizabeth von Arnim
Almanac pairs live weather with public-domain literature. A rainy morning can lead to Walden; snow can lead to Dubliners. Every passage names its book, author, publication year, and the number of years between then and now.
What it does
Almanac reads local weather from MET Norway and classifies it into eight weather groups—clear, cloudy, fog, drizzle, rain, snow, thunder, and wind—and three times of day. That creates 24 reading contexts.
The app selects from 428 curated passages by 27 authors across 32 public-domain books published from 1847 to 1925. No passage is AI-generated. The free shelf contains 86 passages and covers every weather/time bucket. One non-consumable purchase unlocks the other 342; there is no subscription.
The experience is tactile. Only the words move: the page stays paper and ink, with no particles and no sound effects, so the weather lives in the typography itself. Rain drops words onto the page, snow lets them drift, fog brings them into focus, wind pushes them in from the left, and thunder flashes the page. Mobile haptics follow the same timing. Readers can swipe backward through previous passages, and iOS and Android widgets show the current passage without opening the app.
Almanac is for readers who want a quieter alternative to a conventional weather screen: a useful glance at the day that also leaves them with a sentence worth carrying around.
I am submitting Almanac for the RevenueCat Design Award and Ship Kotlin Everywhere (JetBrains) tracks: weather-responsive typography, motion, haptics, and widgets on top of one shared Kotlin selection and data engine.
How I built it
Almanac is a Kotlin Multiplatform project with five surfaces:
- Android app: Compose Multiplatform
- Android widget: Jetpack Glance
- iOS app: Compose Multiplatform inside a small SwiftUI shell
- iOS widget: WidgetKit backed by the shared Kotlin logic
- Desktop: Compose Multiplatform for macOS, Windows, and Linux
Shared Kotlin handles weather parsing, classification, passage selection, SQLDelight content and history databases, and startup. By repository line count, roughly 72% of the Kotlin and Swift application source is in commonMain. The whole Swift side, app shell and widget included, is 179 lines.
Selection is deterministic rather than random at display time. Inputs include local date, time of day, weather group, installation ID, and reading history. Recent passages are removed and the least-read candidates are preferred. Identical inputs return the same passage on every target. This keeps an installation's app and widget aligned while giving different readers their own sequences.
The hash behavior is pinned with golden test vectors, and the selection tests run on JVM, Android, and the iOS simulator. The animation scatter is deterministic too: a word's position supplies its motion seed, while a single page timeline coordinates the headline, words, byline, lightning, and mobile haptics.
RevenueCat manages the core-2026 entitlement for the full shelf. Its ID matches the content pack ID: an active entitlement means the pack is owned. RevenueCat is the source of truth; the local database caches offline access and refreshes after purchase, restore, or offer-code redemption.
Challenges I faced
Making an Android widget look like the app
RemoteViews cannot receive the app's custom Typeface. Text spans and appearances did not solve it; one attempt crashed the widget. I render the “N years ago” heading as a bitmap and keep the passage in a system serif. I also learned that widgets must be checked on a real home screen, not only in previews.
Keeping RevenueCat compatible with the iOS build and tests
With Xcode 26, the KMP RevenueCat interop required Swift compatibility libraries unavailable to Kotlin/Native test executables. The app linked, but the cross-platform tests did not. I moved billing into the app module; the WidgetKit extension sells nothing and links no purchases SDK. This preserved the shared tests without duplicating the product model.
Recovering from an App Review rejection
An early iOS build was rejected under guideline 2.1(a) because a clean launch could remain blank. Startup had waited for location, store state, and weather before drawing. I reversed it: render from a saved or default city, refine when location arrives, and sync the store independently. An unavailable service can no longer erase the interface.
Keeping platforms in agreement
The hard part was normalizing inputs: local ISO dates, lowercase installation IDs, rounded coordinate cache keys, and one shared weather mapping. A controlled demo supplies the same installation ID to Android, iPhone, and desktop to prove identical inputs produce identical results. Production installations keep separate IDs.
What I learned
- Determinism is a property of normalized inputs, not just the algorithm.
- Entitlements are a better source of truth than purchase events; purchase, restore, refunds, offline access, and offer codes can all follow one path.
- The first frame should never depend on a network service or permission prompt.
- Widgets need testing on the actual home screen.
- Kotlin Multiplatform is most valuable in the invisible parts: one weather model, one selection engine, and one test suite that cannot silently drift between platforms.
What I am proud of
Almanac is live on the App Store and Google Play from one Kotlin codebase. It has no account or ads and rounds coordinates to about 1.1 km before storage or transmission. Most importantly, typography and motion are not decoration: they turn weather into the reading interface.
What's next
Japanese passages from Aozora Bunko with a language setting, and a new collection each year as more books enter the public domain.
Log in or sign up for Devpost to join the conversation.