Discovery & rep ride-along
Field shadowing with regional managers, offline-coverage mapping, GDPR + CCPA posture review for customer records, role-based access requirements per territory.
Case study · AgTech · Internal tooling
How we shipped Farm — a native iOS and Android internal app for an agricultural-goods distributor whose field-sales managers spend their days in cellars, warehouses, and out-of-coverage farms across the United States and the European Union. Built on an offline-first SQLite store, a Laravel + React back office, and a plan-vs-actual reporting layer that holds up to GDPR and CCPA scrutiny — and has stayed in continuous production use for three years.
The Farm product team came in with a specific operational pain. Their regional sales managers covered farming territories across the United States and the European Union, and the daily workflow looked nothing like the assumption every off-the-shelf CRM mobile app makes. A rep would drive ninety minutes to a customer site, lose cellular signal inside a barn or a fertilizer storage cellar, talk through pricing for forty-five minutes, agree a deal, and then need to file it — frequently still out of coverage. Existing tooling either silently lost the report or required the rep to remember to refile it from the car. We rebuilt the workflow from first principles as a native iOS and Android internal app: an offline-first SQLite mirror of the product catalog, deal reporting that queues locally and reconciles deterministically, and a regional plan-vs-actual dashboard that the senior manager opens at the start of every week. The result is an internal sales toolkit that ships across the US and EU under GDPR and CCPA / CPRA expectations from day one, sits inside YuSMP Group's portfolio of mobile app development work, and has now run unattended in production for three years.
A snapshot of what the Farm build delivered across iOS, Android, and a Laravel + React back office in its first production cycle and the three operating years that followed.

The platform decision dominated every other architectural choice. We chose native Swift on iOS and Kotlin on Android over a single cross-platform shell because the trade-offs in a three-year production window line up cleanly with the native path. Native clients gave us first-class access to the platform-native Core Data and SQLite stack on iOS and Room over SQLite on Android — the foundation of any defensible offline-first mobile architecture. Background sync semantics differ enough between iOS BGTaskScheduler and Android WorkManager that wrapping both in a single abstraction would have cost us the predictability the operations team needed for unattended reps.
Cross-platform shells — React Native, Flutter, the Xamarin tail — were evaluated and eliminated. Their abstractions over background work and platform-native SQLite were the wrong shape for a rep workflow that spends hours offline. Going native meant the entire stack — UI, local store, sync engine, push-notification delivery — is owned per platform and citable end-to-end against Apple and Google's own developer documentation.
| Dimension | Native Swift / Kotlin (Farm) | React Native | Flutter |
|---|---|---|---|
| Offline SQLite access | First-class — Core Data and Room | Bridged — WatermelonDB or third-party | Bridged — sqflite or Drift |
| Background sync | BGTaskScheduler / WorkManager direct | Wrapped — variable reliability | Wrapped — variable reliability |
| Battery profile on long offline sessions | Tuneable per platform | Heavier — JS bridge cost | Lighter than RN, still abstracted |
| Three-year maintenance cost | Two stable codebases on OS-vendor APIs | Framework upgrade churn | Framework upgrade churn |
| Native accessibility | VoiceOver / TalkBack first-class | Acceptable — partial gaps | Acceptable — partial gaps |
| Push notification reliability | APNs / FCM direct | Bridged — Notifee / RNFB | Bridged — firebase_messaging |
| Per-OS keyboard / locale behavior | OS-native | Wrapped — edge cases | Wrapped — edge cases |
Platform references: Apple BGTaskScheduler documentation, Android WorkManager reference.

The iOS client is built in Swift with SwiftUI for the UI layer and Core Data over SQLite for the local store. The entire deal-reporting surface collapses into a single state machine — draft, queued, syncing, reconciled — and the rep never sees a network error in the user flow because the report has already been persisted locally before the screen finishes its dismissal animation. Customer search runs against the on-device store with no network round-trip, the catalog is paged into a lazy list, and pricing is resolved from the locally cached price-list version that was authoritative at the rep's last successful sync.
The reconciliation path is where most internal sales apps lose correctness, and where we spent disproportionate engineering effort. The flow is: write the report to Core Data with a monotonic client timestamp and a UUID idempotency key, queue it in BGTaskScheduler, attempt sync on every connectivity transition, and let the server return the canonical entitlement record. Duplicates are impossible because the idempotency key is the primary correlation, and a sale closed in a cellar in Sweden and reopened on a train two hours later reconciles cleanly. The end-to-end iOS surface is delivered as part of our iOS and Android engineering practice.

The Android client is written in Kotlin with Jetpack Compose for the UI and a Room layer over SQLite for the local store. WorkManager handles the background sync schedule with backoff semantics that respect Doze mode and Samsung, Xiaomi, OnePlus, and Pixel battery optimizers — without a foreground service, the sync engine would die quietly inside an aggressive OEM container within minutes, breaking the implicit contract that an offline report eventually shows up in the manager's dashboard. A minimal persistent notification keeps the long-form sync alive across Android 10 through Android 14 device families.
The sync engine is where the Android client earns its keep. The on-device delta is shipped to a Laravel API on every connectivity transition, the server returns the authoritative price list and customer roster, and conflict resolution is deterministic — server wins on pricing and customer records, client wins on field notes and rep-attached metadata. After a Wi-Fi to LTE handoff the engine resumes the last sync token automatically, and the catalog refresh is incremental rather than a full reload — a rep with a 250-MB local store does not re-download the catalog every Monday. The same engineering team carries iOS and Android in lockstep as part of our mobile app development practice.

The Farm back office sits on a Laravel API with a React admin and holds only the data it must — customer roster, product catalog, price list, quarterly sales quotas, and the deal-report stream pouring in from the field. There is no per-rep location track, no idle-time keystroke capture, and no metadata pipe to a third-party observability vendor. Counters that drive the regional plan-vs-actual dashboard are aggregated server-side and shipped as anonymous numeric series; per-customer identifiers do not leave the customer table.
Billing identity for the distributor's own customers — the farms and agricultural cooperatives buying the product — is held in the same Laravel store, but is access-gated through a role-based permission model so a field rep sees only their territory's customers. Infrastructure-as-code policies enforce the access invariants — any pull request that would broaden a rep's read scope or introduce a per-rep location log fails CI. The posture is built to align with GDPR obligations for users in the European Union and CCPA / CPRA obligations for users in California and the broader United States — and to make a future independent readiness review a documentation exercise, not an architectural retrofit.
Compliance posture: GDPR-aligned · ISO 27001 ready · SOC 2 Type II in progress · HIPAA-capable · CCPA-acknowledged.
A five-phase build that took Farm from territory-research interviews to production across iOS, Android, and a Laravel + React back office.
Field shadowing with regional managers, offline-coverage mapping, GDPR + CCPA posture review for customer records, role-based access requirements per territory.
Product catalog schema, structured pricing, sync token design, idempotency-key contract for deal reports, Laravel API skeleton, React admin scaffolding.
Swift / SwiftUI iOS client on Core Data + BGTaskScheduler; Kotlin / Jetpack Compose Android client on Room + WorkManager; deal-reporting state machine.
Infrastructure-as-code policies that block access-scope regressions, role-based permission QA, crash-telemetry pipeline, three-year operating envelope review.
App Store + Google Play internal-distribution submission across US and EU storefronts, regional-dashboard rollout, plan-vs-actual cutover for senior managers.
Farm's reporting layer was built to keep field reporting and manager analytics provably aligned, because the plan-vs-actual loop falls apart the moment a manager and a rep see different numbers for the same customer in the same week. The quarterly quota is set by a senior manager in the React admin and pushed to each rep's local store on the next sync. The rep sees their own progress in the mobile app — a single sparkline against the plan, broken down by product category — and the senior manager sees the regional aggregate in the back office with a drill-down to per-rep and per-customer breakdowns. Server-side aggregation runs against a single authoritative deal-report stream that the mobile clients feed via the idempotency-key path, so the manager view and the rep view always reconcile to the same source of truth. Kill-edit behavior, late-arriving offline reports, and rep-attached field notes all read from the same record, so a single deal resolves cleanly across reinstalls, device swaps, and the eventual web portal. The whole subsystem was built with extensibility in mind: adding a multi-currency tier, a per-territory commission engine, or a B2B partner-rep extension is a configuration change against the back office, not a code release.
Farm launched on Apple App Store and Google Play (internal distribution) with storefronts active across the United States and the European Union. The English-language build serves field-sales reps in California, New York, Texas, Florida, and Washington in the US, and in the Netherlands, Germany, France, Ireland, and Sweden in the EU, without a separate codebase per region. Consent flows are region-aware at the back-office layer: rep accounts in the EU and EEA receive a GDPR-style granular consent screen for any optional product telemetry, and accounts in California receive a CCPA-style "Do Not Sell or Share My Personal Information" disclosure in the same enrollment flow. Data-handling practices are aligned with GDPR for European users and with the US state-privacy patchwork — CCPA / CPRA (California), VCDPA (Virginia), CPA (Colorado), CTDPA (Connecticut), UCPA (Utah), TDPSA (Texas), and Oregon CPA. Because the architecture minimises per-rep tracking data, regional compliance reduces to honest disclosure rather than per-jurisdiction data segregation.
The Laravel API was rolled out across EU and US regions in parallel — Netherlands, Germany, France, Sweden, and Ireland for EU coverage; US East and US West for North America — with each region's stateless workers provisioned identically. The aggregation service that drives the regional plan-vs-actual dashboard runs stateless workers that can be pinned to EU or US regions independently for future data-residency commitments. Both the App Store and the Google Play distribution were calibrated for internal-enterprise enrollment, and the in-app privacy disclosure documents exactly the architecture above, citing GDPR obligations and California CCPA obligations directly. The engineering team behind the build sits across CET and runs a CET workday with East-Coast US overlap (9 AM–1 PM ET) for stand-ups, store-review choreography, and incident response — the timezone that lets a US product team and an EU engineering team share four hours of live overlap every day.
The active custom software development roadmap for Farm includes a per-territory commission engine, a multi-currency price list for cross-border distributors, an obfuscated offline-share transport so reps can hand a deal report from one device to another in the field without round-tripping through the server, and a desktop client built on Tauri to share business logic with the mobile codebase. A B2B partner-rep tier with bring-your-own-customer-list, team management, and SSO is planned for US and EU mid-market distributors, with the entitlement subsystem already structured for multi-seat assignment. Infrastructure plans include further cloud & DevOps automation of the regional-rollout pipeline, an internal continuous-verification harness for the offline-sync correctness contract, and a future independent readiness assessment scaffolded into the operations cadence.
If you are planning an internal field-sales mobile app, an offline-first AgTech tool, or any mobile app where reps spend hours out of coverage and the reporting loop has to survive an outside auditor for audiences in the US and EU, we have shipped this stack end-to-end and can compress the build timeline meaningfully. The live product is operated internally by the distributor at yusmpgroup.ru/cases/farm (case page), and the engineering team behind it sits inside YuSMP Group. We work fixed-price for well-scoped MVPs and on dedicated development teams for ongoing delivery, with a CET workday and a guaranteed East-Coast US overlap (9 AM–1 PM ET) window for stand-ups, demos, and incident response.
An internal field-sales mobile app with iOS and Android clients, an offline-capable product catalog, deal reporting, and a Laravel or Node back office typically costs $80k–$180k for an MVP. Adding regional plan-vs-actual dashboards, role-based permissions, conflict-free offline sync, push reminders, and integrations into an existing ERP or 1C-style accounting stack brings a full-featured rollout to $220k–$450k. The dominant cost driver is the offline-sync correctness work and the per-region pricing logic.
Field-sales reps in agricultural territories work for hours offline, in cellars, warehouses, and fields with no signal. Native Swift and Kotlin clients let us control battery use during long sync sessions, integrate platform-native SQLite stores cleanly, and ship per-OS keyboard and accessibility behavior that React Native and Flutter still wrap in compromises. With a three-year operating window in mind, the maintenance cost of two native codebases turned out lower than the cumulative cost of working around cross-platform abstractions.
A defensible offline-first posture is an architecture decision, not a feature toggle. The full product catalog, pricing tables, and customer list are mirrored to a local SQLite store at first login and incrementally updated on every successful sync. Deal reports written offline are queued with monotonic local timestamps and reconciled on the server using idempotency keys, so a rep who closes a sale in a basement and reopens the app on a train two hours later never duplicates or loses a transaction. Conflict resolution is deterministic — server wins on price, client wins on field notes.
Farm is the internal toolkit a regional sales manager carries into a farm visit. It exposes a structured catalog of fertilizers, seeds, chemical agents, and equipment with current pricing; lets the rep file a deal report in seconds against a known customer; tracks plan vs actual against the rep's quarterly sales targets; and surfaces a manager-side dashboard that breaks the numbers down by product category and region. Push reminders nudge unfiled reports, and a back-office admin in React handles catalog updates, pricing changes, and quota assignment.
A focused MVP with native iOS and Android clients, an offline-first catalog, deal reporting, and a Laravel admin typically takes 14–20 weeks. Adding the manager-side regional dashboard, the plan-vs-actual analytics, role-based permissions for senior managers and field reps, and the conflict-free sync layer adds 6–10 weeks. Hardening the app for three years of unattended production use — crash telemetry, push reliability, sync replay logs — is frequently underestimated and should be budgeted at 4–6 weeks of dedicated work.
Related cases
Field-audit mobile app for inspectors — offline-capable forms, multi-photo capture, manager dashboard.
View case → Retail Ops · MobileInternal training and performance app for a retail chain — knowledge base, performance tracker, back office.
View case → Auto Parts · B2BB2B auto-parts marketplace with supplier CRM, structured catalog, and order workflow across the US & EU.
View case →