Mobile
GrowBook: my reminder app's one job was to remind you. It didn't.
shaojie gong DEV Community
2 views
My app has exactly one job it cannot get wrong: fire a reminder so you water your plant. It's a to-do notification. How hard could it be.
It took me a week, and by the end I understood why so many reminder apps quietly don't work.
Here's the thing nobody tells you: on modern Android, "schedule a notification for 9am tomorrow" is not one problem. It's at least three, and each one fails silently, which is the worst way for anything to fail.
Problem one: permission to notify at all. On Android 13+ you have to request POST_NOTIFICATIONS at runtime, like camera or location. Skip it and your notifications just… don't appear. No error. The user thinks the app is broken; the app thinks it's working. So the first thing I built wasn't the notification — it was a little dialog that explains why I want to notify you before the system prompt pops, because a cold permission request for a plant app gets denied every time.
Problem two: exact timing. I schedule reminders at a specific minute, and Android 12 introduced a whole separate permission for that — SCHEDULE_EXACT_ALARM. Without it, the OS is allowed to batch your "alarm" whenever it feels like it to save battery, so your 9am watering reminder shows up at 1pm when the phone wakes up for something else. "Around 9-ish" is useless for a reminder. So there's a second permission flow, and it can't even use the normal runtime prompt — it kicks the user out to a system settings screen. I have to detect whether it's granted and walk them there by hand.
Problem three, the one that actually broke me: the reminder has to survive a reboot. Scheduled alarms are wiped when the phone restarts. The fix is a BootReceiver — you register for BOOT_COMPLETED and reschedule everything when the phone comes back. Standard. I wired it up (the flutter_local_notifications plugin ships one), and it worked on my test phone.
Then I tested on a cheaper Android phone with a heavy custom skin, and nothing fired. At all. Not late — never.
This is the part of Android development that isn't in the docs. A lot of phones — especially certain popular brands — aggressively "optimize" battery by straight-up killing background apps and refusing to let them wake up, BootReceiver or not. Your app is treated as a misbehaving process for the crime of wanting to run a scheduled task. There is no clean API that fixes this. The only lever you get is asking the user to exempt your app from battery optimization (REQUEST_IGNORE_BATTERY_OPTIMIZATIONS), which is yet another permission and yet another trip to settings.
So my "one simple notification" became a chain of three permission requests and a battery-optimization plea, each with its own explanation dialog, each degrading gracefully if denied, in an order I had to tune so I don't ambush a new user with four system prompts in a row the second they open the app.
The receiver registration in the manifest, for anyone who's fighting this right now, ended up needing more than just BOOT_COMPLETED — I also register for MY_PACKAGE_REPLACED (so reminders survive an app update, not just a reboot) and the QUICKBOOT_POWERON actions that some skins fire instead of the standard one. Miss those and a chunk of your users silently lose every reminder after an update.
What I keep taking away: "it's just a notification" is the kind of sentence that eats a week. The happy path is five lines. Everything real is in the four ways the platform will quietly refuse to run those five lines, and none of them throw an error you can catch. You find them by testing on the ugly, cheap, real phone — not the clean one on your desk.
If you've shipped reminders on Android: what's the sketchiest thing you had to do to make them actually fire? I have a feeling everyone who's done this has a battery-optimization war story.
— building GrowBook in public, #2
Read original: https://dev.to/shaojie/growbook-my-reminder-apps-one-job-was-to-remind-you-it-didnt-1h20
← Previous
Enterprise-managed sandbox in Copilot for JetBrains
Next →
From a Synthetic AHP Event to a Real Base Sepolia Anchor
Related
SSH through multiple jump hosts: a practical guide for Mac, iPhone, and iPad
Mobile
6
Dev.to (EN Zone)
Notes on the Pitfalls of iOS Sandbox Accounts and Apple Pay
Mobile
6
DEV Community
40 Million Fake Push: When Spam Commits Took Over The Public GitHub
Mobile
7
DEV Community
What iOS Build Tools Are Available: From xcodebuild to KXApp Compilation Solutions
Mobile
5
DEV Community
Comments0
No comments yet — be the first