Skip to content
Arrit Gashi
← All work
Shipped2026Mobile app

Tend Daily

Tells a home gardener what needs watering today, in one notification, entirely offline.

The Tend Daily store banner: a white two-leaf sprout on a green rounded square beside the wordmark Tend Daily and the line Know what needs water today, on a pale green background.
The Google Play feature graphic. The only landscape asset the project has, so it is the cover; the screens are below.
unit tests passing
308
languages, fully localized
3

The problem

Every watering app on the store is an indoor houseplant tracker that repeats a fixed interval all year. None of them cover the lawn, the fruit trees, the raised beds and the pots on the deck, and none shift the schedule with the month or the climate region. So the person with an actual yard either gets nagged plant-by-plant until they delete the app, or keeps the whole schedule in their head.

What I built

An offline-first app with no account, no login and no backend. The entire product logic lives as pure TypeScript importing nothing from React, React Native or Expo — which is why it can be tested with plain Jest and no native mocks, and why the tests were written before any screen existed. The watering interval is recomputed on every read rather than cached, because the seasonal multiplier moves with the month.

Three inputs carry the real-world accuracy. Placement, because a potted tomato dries three to five times faster than the same plant in the ground. Rain, as one button that resets everything outdoors and correctly never touches indoor plants. And dormancy, which suppresses a fig in a Minnesota January entirely rather than showing a 45-day countdown — a countdown invites the user to water a dormant root ball.

Around that: a calendar deriving twelve months of jobs from what the garden actually holds, a frost clock built from bundled climatology rather than forecasts, and a weather diary that records every fetched forecast day so a year of ordinary use turns into an almanac without ever paying for historical weather data. English, Spanish and Chinese throughout.

The hard part

Project thirty days of alarms, not one

The app promises a single daily notification saying how many things need water today — content that cannot exist until the moment it fires. But a repeating local notification has static content, the iOS extension that could rewrite it only intercepts remote push, and background execution is unreliable on iOS and actively hostile on several Android builds.

So instead of one repeating alarm, the engine simulates the next thirty days assuming nothing gets watered, and every day with something due becomes an ordinary one-off notification with its body text already composed. Thirty sits comfortably under the iOS limit of sixty-four pending notifications. The cost is that the whole queue is now derived state that must be torn down and rebuilt whenever anything affecting it changes, so rebuilds are debounced — watering twelve plants in a batch costs one rebuild rather than twelve. The alarms are also deliberately inexact: exact-alarm permission is restricted from Android 12 onward, and watering does not need second precision, so both exact-alarm permissions are explicitly blocked rather than requested and defended.

Outcome

  • Live on the US App Store at $3.99.
  • 308 unit tests passing, including a simulation of a full year of following the reminders across all six climate regions, asserting no plant is ever left more than a day past its interval.
  • An accessibility audit left two source-level regression guards behind: no hard-coded font size below the legibility floor, and no text opted out of system font scaling.
  • A contrast script computes the WCAG ratio for every foreground on every surface in both themes and fails the build below threshold — its first run found 15 failing pairs.
  • 24 store screenshots generated from the app's own theme tokens and string catalogs, replacing a hand-written set that had silently drifted out of date.

Screens

  • The Today screen under the headline Know what needs water today: a greeting, the line 4 things need water today, a weather card reading 88 degrees and sunny with a seven-day strip, a note that rain is likely tomorrow so holding off is fine, then three plants due with Water buttons, and two actions: Water these 3 and It rained.

    The whole promise on one screen: what needs water, why, and one button to say it rained. The rain button resets everything outdoors and never touches the kitchen window.

  • The Today screen under the headline It knows when rain is coming, in its empty state: Nothing to do today, a note that everything is watered and basil is next in two days, and a frost card reading First frost is coming, typically around October 18 here, about 21 days out, with advice to bring the tender pots inside.

    The frost clock runs on bundled climatology, not a forecast, so it works offline and a year ahead. It names the pots that need to come in.

  • The Garden screen under the headline Grouped the way your yard is: plants listed under Front yard, Back yard and Kitchen window, each with its next watering, from 1 day late to In 6 days, and the hint Swipe a row to water it.

    Placement is the first real-world input. A potted plant dries three to five times faster than the same plant in the ground, so where it sits changes the interval.

  • The Calendar screen under the headline The whole year, month by month: 2026 for the Transition region, with July and August showing recorded temperature ranges and wet days, jobs such as Check the lawn for grubs and Feed the vegetable beds, and October noting the typical first frost.

    Twelve months of jobs derived from what the garden actually holds. The past months show recorded weather, which is the weather diary surfacing as an almanac.

  • A single plant screen for Front lawn under the headline A year of care for every plant: tags for tall fescue, in the ground and full sun, the line Water about every 4 days with next due today, a Watered it button, a month-by-month job list from March to November, and a history showing a watering on August 24 and rain on August 21.

    Every plant carries its own year and its own log. The interval is recomputed on every read, so it moves with the month rather than being cached.

Want something like this built? I take on a small number of projects at a time.