Rev
Airple is an aircon servicing company in Singapore. Its app lets customers book a cleaning or repair, pay for it, and watch the technician on the way to their door. I was the only mobile developer on the project, working with the team at Hyge, and built both React Native apps: the customer app on the App Store and Google Play, and the internal app technicians use on the job.
Between July 2024 and April 2025 that came to about 1,250 commits across the two apps. Teammates built the NestJS backend, the admin panel and the customer website. Everything that ran on a phone was mine, from talking requirements through with the client, our PM and the designer to shipping the release and fixing what came back.
Booking an aircon service is a fairly normal e-commerce flow. What customers really care about is when the technician will show up, and that question ended up pulling in background location, maps, real-time ETAs and, eventually, Live Activities.
The customer app's home screen. Screenshots from the App Store listing.
The customer app's home screen. Screenshots from the App Store listing.

What I built

Booking, contracts and payments

The customer app covers the whole order: browse services, add units to a cart, apply vouchers, check out, reschedule, and review the technician afterwards. A lot of the work was in the details the business ran on, like service contracts, priority scheduling, admin fees, credit refunds, and service reports customers can open as PDFs.
Sign-in changed twice along the way and ended up as WhatsApp OTP plus Apple and Google sign-in, with reCAPTCHA in front. Every push notification deep-links to the screen it's about, whether that's a review request, a refund or an order.
Booking a service.
Booking a service.

Live technician tracking

Tracking is the part I'm proudest of, because when I joined it barely worked. The technician app already sent locations, but every new position opened a brand-new socket connection just to send that one point. Connections piled up and dropped, and the call even had a comment above it that read DISABLE SOCKET NOW TO PREVENT BUG. The background location task was commented out, so tracking stopped as soon as a technician switched apps or locked their phone, which is exactly what happens while they're driving. The route was fetched once and the ETA never changed after that. For customers, it was a map they couldn't trust.
I rebuilt it in three steps:
  • One connection per trip (Oct 2024). I rewrote the tracking screen around a single socket hook. It opens when the technician sets off, stays open until they arrive, closes when the screen goes away, and sends a position whenever the coordinates change. I also turned on the iOS and Android background location modes.
  • Live routes and ETAs (Nov 2024). Each position now carries speed and heading. When the technician's position no longer lines up with the route, the app fetches a new one instead of drawing a line they've already left, and both apps recalculate the ETA in real time. The customer app keeps tracking state in MMKV, so reopening it doesn't start from an empty map.
  • Background updates (Jan 2025). A background location task, registered with expo-task-manager, keeps reporting the technician's position for the active job while the app is in the background or the phone is locked.
TaskManager.defineTask(TECHNICIAN_TRACKING_DEFAULT_TASK_NAME, async ({ data, error }) => { if (error || !data) return; const location = data.locations[0]; const scheduleId = useTrackingStore.getState().activeScheduleId; if (!scheduleId) return; await backgroundUpdateTechnicianLocation(scheduleId, { latitude: location.coords.latitude, longitude: location.coords.longitude, }); });
Technician app, src/utils/location.js (trimmed)
That's the version customers use today, and real-time technician tracking is now the first feature on the App Store listing.
Tracking the technician's arrival.
Tracking the technician's arrival.

Live Activities, before React Native had a good answer

Nobody keeps a tracking screen open while they wait for a technician. I wanted the arrival on the Lock Screen instead, and in 2024 there wasn't a React Native library for Live Activities that I trusted. So I wrote one for the app: a local Expo module in Swift, plus a widget extension added through expo-apple-targets.
The Lock Screen view mirrors the in-app tracker, with a progress bar and a small car moving toward the customer's address, the current status and the estimated arrival. Each activity is keyed by its schedule ID, so starting one twice does nothing, and an update for an activity that doesn't exist yet starts it.
The harder part was making it work while the app isn't running. Live Activities can be started and updated through APNs, but that needs tokens from the device. On iOS 17.2 and later, the module listens for push-to-start tokens and per-activity push tokens and passes them to JavaScript, which sends them to the backend. I wrote the server-side triggers too, so from then on the server drives the activity. It appears when the technician sets off and updates as they get closer, without the customer opening the app.
if #available(iOS 17.2, *) { Task { for await pushToken in Activity<AirpleAttributes>.pushToStartTokenUpdates { let pushTokenString = pushToken.reduce("") { $0 + String(format: "%02x", $1) } NotificationCenter.default.post(name: .onStartPushTokenChange, object: pushTokenString) } } }
AirpleTrackingLiveActivitiesAppDelegate.swift (trimmed)
Later I added a compact layout for the Apple Watch Smart Stack, and passed a deep link through the activity so tapping it opens that order's tracking screen. I published the approach as expo-live-activities-demo, an open-source example of server-driven Live Activities with Expo Modules. The watch layout is also the same problem I later solved upstream, in Voltra's supplemental activity families support.
The tracking Live Activity in the Apple Watch Smart Stack.
The tracking Live Activity in the Apple Watch Smart Stack.

The technician app

The technician app is the other half of every order. Technicians see their jobs for the day, travel to each one, run a safety check on arrival, record payment (including PayNow), and submit a service report for each aircon unit with photos and the unit's brand and model. Reports turn into PDFs the customer can open from their order history. I also built the screens for missed reports and technician KPIs.

Being the only mobile engineer

There was nobody to hand the awkward parts to. I worked directly with the client, our PM and the designer through the whole cycle: turning a request into something buildable, going back and forth on designs, estimating, building, getting it through store review, and supporting it once it was live. When something broke in production, I was the one who got the message and the one who fixed it.
Both apps ran on Expo with EAS builds and over-the-air updates through expo-updates, with Sentry for crashes and Firebase for push notifications and analytics. As the only mobile developer, I also handled releases to the App Store and Google Play. The customer app launched in September 2024 and is still live, now at version 1.1.16 with a 4.4 rating on the Singapore App Store.

What I learned

Being the only mobile engineer taught me most of what I know about mobile development. I owned the whole lifecycle, from requirements with the client and PM and designs with the designer to the code, builds, store review, crash reports and the follow-up fixes. You learn quickly which shortcuts come back to bite you when you're also the person who gets the bug report.
Tracking is a pipeline, not a screen. A good arrival estimate depends on the technician's phone reporting location reliably in the background, the server keeping up, and the customer's phone getting updates while it's locked. Each step fails in its own way, and the customer only ever sees the end result.
Live Activities also showed me where React Native stops. Some features only exist in Swift, so the job is to build a small, well-shaped bridge and keep the native side boring. Writing that module is why I'm comfortable with Expo Modules today, and it's where my later contributions to Expo and Voltra started.