Offline Is Not an Edge Case
Founders treat connectivity loss as a rare fault. For a physical product it is a normal operating condition. Your device sits in a basement utility room, a walk-in cooler, a boat, a job site trailer, a rural barn, or a hospital wing where the guest Wi-Fi drops every few minutes. The user's phone loses LTE in a parking garage. The home router reboots after a firmware update. The customer's ISP goes down for six hours.
In field data from connected consumer products, a meaningful share of daily sessions include at least one period with no usable internet path. If your app shows a spinner and a "check your connection" banner during those periods, the customer concludes the product is broken, not that the network is. Support tickets and one-star reviews follow, and neither of them says anything about Wi-Fi.
The Critical Split: Local Path vs Cloud Path
The single architectural decision that determines how well your app degrades is whether the phone can talk to the device directly. If every command routes phone to cloud to device, then no internet means no product. If the phone can reach the device over BLE or over the local network, most core functions survive.
Draw the matrix early. For each feature, ask which path it needs: local only, cloud only, or either. Turning the device on, changing a setpoint, reading current sensor values, and running a calibration should all be local. Historical charts, firmware updates, multi-user sharing, and account management are legitimately cloud functions. Anything you can move to the local column is a function that keeps working when the network does not. The full tradeoff is laid out in local control vs cloud control, and the link choice itself is covered in Bluetooth Classic vs BLE.
A common and effective pattern is BLE as the always-available control channel and Wi-Fi or cellular as the telemetry and update channel. The user can always do the important thing standing next to the product; the analytics catch up later. That does mean the app has to keep BLE credentials and pairing state cached locally, so the first thing it does on launch is not a cloud auth call.
What the App Side Needs
- A local database, not a cache. SQLite, Room, Core Data, or Realm holding device state, settings, and recent readings, written on every change. The UI reads from the local store; the network layer syncs it. If your UI reads directly from HTTP responses, offline mode is a rewrite, not a feature.
- An outbound command queue. User actions that need the cloud get persisted with a timestamp and retried with exponential backoff. The user sees "pending" state, not a failure dialog.
- Honest state indicators. Three distinct visual states: connected to the device, connected to the cloud, and stale data with a timestamp. "Last updated 14 minutes ago" prevents more support calls than almost anything else in the UI.
- Conflict resolution you chose deliberately. If the same setting was changed on the device keypad and in the app while offline, decide in advance whether last-write-wins by timestamp, device-wins, or prompt-the-user. Undefined conflict behavior surfaces as random setting reversions.
- No auth blocking on launch. Cache a long-lived token or allow a local-only session so an expired cloud login does not lock a user out of their own hardware.
- Bundled assets. Onboarding art, help text, and unit lists shipped in the binary rather than fetched, so first-run in a dead zone still works.
What the Device Side Needs
Offline is a firmware requirement before it is an app requirement. The product must complete its primary job with no phone present and no network at all. That means setpoints, schedules, and safety limits are stored in device flash, not fetched at boot.
Add a local ring buffer for telemetry. Size it against a realistic outage: a sensor logging one record every 5 minutes with a 32-byte record needs about 280 KB to cover a month, which is nothing on modern flash. Timestamp records with a monotonic counter plus a real-time clock, and re-anchor to true time on the next sync so a dead RTC does not corrupt your history. Then upload in batches when the link returns; the protocol tradeoff sits in MQTT vs HTTP for IoT, and how the device joins the network in the first place is in Wi-Fi provisioning from an app.
Design the reconnect behavior with a backoff and jitter. Ten thousand units that all lost cloud connectivity in the same regional outage will otherwise reconnect in the same second and take down your ingest tier. And never let a failed cloud call block the control loop; that path is how a thermostat stops heating because a certificate expired.
Three Levels of Offline, as a Product Decision
Level 1, graceful degradation. Core control works locally, cloud features show a clear disabled state, nothing is lost. This is the minimum bar and adds roughly two to four engineering weeks over a cloud-only build.
Level 2, full local operation with deferred sync. Everything except account and sharing features works, data is buffered on both device and phone, and reconciliation happens later. Budget six to ten weeks, mostly on sync and conflict logic.
Level 3, offline-first. The cloud is an optional accessory; the product is fully usable by a customer who never creates an account. This is the right call for industrial, marine, agricultural, and regulated deployments, and it shapes the whole architecture. Decide the level during requirements, not after the first field complaint, and record it in your PRD alongside the question of whether the product needs an app at all.
Testing It Honestly
Airplane mode is the weakest possible test, because it fails cleanly and instantly. Real networks fail badly. Test the captive portal that returns a login page instead of your API, the connection that stays associated with zero throughput, 900 ms latency with 20 percent packet loss, DNS resolving but TCP hanging, and the transition mid-transaction from Wi-Fi to cellular. A network link conditioner and a router you can throttle will find more bugs in an afternoon than a month of clean-lab QA. Add a soak test: leave a unit offline for 72 hours, then restore the link and verify every buffered record arrives once, in order, with correct timestamps. Watch the power draw during the retry storm too, because aggressive reconnect logic can quietly halve battery life, as covered in low-power firmware design.
Build a Product That Survives the Dead Zone
Projects House designs connected products where firmware, app, and cloud are specified together, including the offline behavior that decides how the product feels on a bad day. Describe your device and where it gets deployed through our contact form and we will map the local and cloud paths with you.