TL;DR
I owned Mission Cultivate's iOS and Android apps. I took a Capacitor prototype to production releases on both stores, then built the release pipeline, security hardening, and version-compatibility system that keep the apps shippable.
The Problem
Mission Cultivate is a community platform that existed only on the web. A Capacitor prototype showed the React app could run natively, but it wasn't a product yet. Android lagged behind iOS. There was no deep linking, no native file handling, and no automated release process. Nothing stopped an old app build from calling an API it no longer worked with. Users who expected a real mobile app felt this, and so did the team, because every release was manual and fragile.
What It Does
One React codebase ships as a web app, an iOS app, and an Android app. Each native app has environment-specific builds and behaves like a native app rather than a wrapped website.
- Universal Links and App Links open shared Mission Cultivate URLs directly in the app.
- Push notifications on iOS and Android, including chat messages.
- Native file uploads, downloads, and sharing, including a camera and system photo picker on Android.
- Android back-button behavior that matches what users expect.
- Forced-update and "update available" prompts when the installed app is too old for the API.
My Role
I was senior software engineer on a team of 3 engineers, and the primary owner of the native platform. The CTO built the initial Capacitor integration as a proof of concept, but from March 2026 I owned what came next: iOS and Android feature parity, platform configuration, store releases, CI/CD, security, and the backend contracts the apps depend on.
Key Contributions
- Built deep linking across both platforms: Apple associated domains and the
apple-app-site-associationfile, Android verified App Links, and per-environment custom URL schemes.
- Led the Android catch-up. I added Gradle product flavors for local, dev, and prod builds, along with icons, deep-link handling, and FCM push registration, so Android reached parity with iOS.
- Designed an Android back-navigation policy. Overlays dismiss first, logged-out screens never go back into authenticated content, and the app exits cleanly from the home feed.
- Hardened Android release builds for Play Store submission. This covered minification and obfuscation, network security config, backup restrictions, tapjacking protection, and release-only logging. I checked the work against Google's security and privacy checklists, and limited it to release builds so debug builds stay easy to inspect.
- Replaced the Android WebView's Google Photos-only file input with a native picker that offers camera, photos, or files. It uses a narrowly scoped
FileProvider, and iOS keeps PHPicker so it never needs a photo-library permission.
- Automated releases with Fastlane and GitHub Actions. iOS builds go to TestFlight and Android builds go to Play Internal Testing, in both dev and prod.
- Added a sandbox build variant that uses the dev identity but points at the sandbox environment, for safer QA.
- Shipped native mobile sign-up, file download and share, Apple privacy manifest updates, and keyboard and layout fixes.
Technical Highlights
- API compatibility across app versions. Store-installed apps can't be updated on demand, so an old build could break silently against a newer API. I designed a version handshake. Every request reports the app's platform and version. The Rails API returns per-platform minimum and target versions in response headers. The client then shows a blocking update screen below the minimum, a dismissible banner below the target, or a one-time reload on web. A throttled observation table records which users are still on outdated builds. A separate gated path rejects older iOS builds that can't read the headers.
- Security hardening limited to release builds. I used Android's source-set merge order, where build type overrides flavor and flavor overrides main, to put all hardening in
src/release/. Production and QA builds get the full hardening, and local debug builds stay debuggable. Automated tests check the security config so it can't quietly regress.
- Push notifications on both platforms from one codebase. I made the notification registrar work with both FCM and APNs, pointed the config at the right environment for each flavor, and routed notification taps to the correct screen, including chat deep links.
Impact
- Shipped iOS and Android releases, for example iOS 1.0.3 and Android 1.0.1.
- Releases went from manual Xcode and Android Studio builds to a single CI-triggered Fastlane lane per platform and environment.
- The compatibility gate lets the backend change safely without breaking users on old app versions.
What I Learned
A wrapped web app is only as good as its platform seams: back navigation, deep links, file pickers, and version skew. The cross-platform code was the easy part. The work that mattered most was making the app feel native and keeping its release process sustainable.

