PoultryPal Learns Android in a Week, LuckyCoin Sheds Its Skin
- Projects
- 4
- Commits
- 74
- Lines
- +23k / −7.2k
- Files
- 328
- Claude time
- 17h
- 27h of work
- Tokens
- 6M
Claude time is wall-clock at the keyboard; the hours of work below it is total model effort, which runs higher when several Claude windows work in parallel.
Commits by day
Where the work went
LuckyCoin54%
PoultryPal46%
Two old throughlines did most of the work this week: closing the iOS/Android gap on one app, and digging years of cruft out of another. PoultryPal went from iOS-only to a full Android app, and LuckyCoin spent the week being rebuilt from the inside out.
PoultryPal lands on Android
PoultryPal has existed on iOS for a while. As of Monday it did not exist on Android at all. By Sunday it does, and not as a hollow shell — it's the actual app.
The sequence was about as linear as these things get. We started with empty Android Studio scaffolding and a foundation commit (dependencies, the Room database schema, theme, navigation shell), then ported the features over one at a time: Flocks and Birds, egg tracking, the journal, finances, hatching runs (with WorkManager handling reminders and the Eggs tab segmenting accordingly), sales wired to mirror into finances, the dashboard, and settings with CSV export and dark mode. Then we went back through and connected the seams — cross-tab deep links so a leaderboard or related row jumps to the right bird or flock, the Bird-detail "Add Egg Record" and "Add to Hatching Run" buttons, the Journal and Finances tabs on Flock detail.
With the features in place, the back half of the week was making it look and behave like the iOS version rather than a generic Material app: the warm amber theme with dynamic color turned off, a dashboard hero card matching the iOS layout (and then de-framing it when the gradient started reading as a card-inside-a-card), the brand icon and the hen replacing placeholder launcher art and leftover paw-prints, and the iOS-only behaviors that are easy to forget — onboarding, the launch paywall, the review prompt, notification recovery. The live RevenueCat key and paywall went in, and the applicationId now matches the iOS bundle id, which is the kind of detail you only get one clean shot at.
A couple of decisions there are worth calling out because they're the difference between an app that survives and one that quietly corrupts itself. Photos now live as files on disk instead of as BLOBs in the database, so Android's Auto Backup actually stays reliable. The minSdk dropped to 26 (Android 8.0) to widen the device net. And we wrote real tests — instrumented coverage for CSV export, the image GPS-strip, and the reminder worker, plus a suite for the highest-stakes data-integrity and formatting logic. For an app that tracks people's flocks and money, that's the right place to spend the test budget.
Meanwhile the iOS side shipped 1.3.1: premium gate hardening, stripping EXIF (including GPS) from photos, and delete actions in the edit views — the same GPS-strip concern that then got ported and tested on Android.
LuckyCoin gets taken apart and put back
If PoultryPal was construction, LuckyCoin was demolition and renovation. The headline number is that we removed more lines than we added, which for a years-old app is usually a good sign.
Most of the effort went into Swift 6 strict concurrency, done in labeled phases so it stayed traceable rather than turning into one giant unreviewable diff. The warning count came down in stages — 222 to 46 across the Tier 1 work, then 46 to 2 once we inferred Sendable from captures — by pushing the right things onto the main actor, converting managers like the eBay and metal-price lookups into actors, and turning a long string of Firestore completion-handler callbacks into plain async/await across the friends, coin grid, and category views. In user terms: fewer threading footguns, and groundwork for code that won't fight the compiler going forward.
The other big piece was breaking up UserManager, which had quietly become the place everything lived. Coin pricing moved out into PricingManager, push notifications into a dedicated PushNotificationService, the RevenueCat lifecycle into SubscriptionService, and the Firestore key strings into their own namespace. Along the way we deleted a download chain and a pile of dead toggle methods, dropped seven dead functions out of SubCategory alone, and unified two duplicate RevenueCat implementations into one. None of this changes what the app does; it changes how much of the app you have to hold in your head to change anything, which is the whole point of unwinding a god-object.
There were also concrete fixes a user would actually feel. The category and country lists were recomputing running totals on every single row appearance — fine on a short list, miserable on a long one — so we snapshot the totals and compute them in the background instead. The Stats tab had the same disease and got the same medicine: compute off the main thread, and short-circuit entirely when the inputs haven't changed. Scrolling should be noticeably less janky.
Rounding it out: guest auth, a brand-color migration, cross-platform analytics constants (a paywall funnel and domain events, shared with the luckycoin-next web side), print swapped for a debugLog that stays quiet in release builds, and dependency bumps for Firebase, RevenueCat, and Lottie alongside Xcode 16.5's recommended settings.
Two apps, two opposite kinds of progress: one gained an entire platform, the other lost a lot of weight it had been carrying. Both move the portfolio toward the same place — apps that behave the same wherever you run them and don't collapse under their own history.


