Telemetry That Does Not Send Location Coordinates to the Server
For a login-free app, I first decided what not to collect and kept only operational signals
Hangangjari is a login-free app. Even so, it needs minimal telemetry to know whether the app is working correctly. Here, telemetry is not a record for tracking users. It is a signal for app operation: whether widgets are used, whether notification settings are saved, and whether the API is slow.
The problem was not first deciding what to collect. It was deciding what not to collect.
Once telemetry is added, it grows quickly. It becomes tempting to know how often users tap a button, where they leave, or which parks they often view.
But Hangangjari is a login-free public-information app, and it naturally has location context. So I first chose what not to see before choosing what could be seen.
Location stays on the device
Hangangjari needs location when sorting nearby parks or parking lots. Even then, the current coordinates are not sent to the server.
sequenceDiagram autonumber participant User as User participant App as iOS app participant Location as Location service participant Server as Server API User->>App: View nearby places App->>Location: Request current location Location-->>App: Coordinates or nil App->>App: Sort parks/lots App-xServer: No coordinate upload
If location permission is missing, the app receives nil and only nearby sorting is limited. The server API returns park and parking-lot data, but it does not store the user’s current coordinates.
This choice reduces convenience a little, but it is much easier to explain.
Doing nearby sorting on the server might make analysis and iteration easier. But the cost is that location coordinates pass through the server. Hangangjari chose the side that can say, “location is used only on the device.”
Anonymous events
Events are based on an anonymous install hash, not an account. The app stores a seed on the device and creates a hash with a scope.
The server does not receive names, email addresses, advertising identifiers, or location coordinates. It receives only the minimum fields needed to observe app operation.
Representative fields are:
| Field | Purpose |
|---|---|
| event type | What happened |
| surface/entrypoint | Whether it came from app, widget, or notification |
| park ID / lot ID | Which park or parking-lot context |
| information freshness bucket | Whether the information seen was fresh or old |
| duration/status/reason | Slow points and failure reasons |
| anonymous install hash | Distinguishes install units without accounts |
This is enough to answer whether the app is working properly. It does not need to identify people or create a location history.
Even park IDs and lot IDs need care. They are useful for fixing the app, but they should not be used to reconstruct a person’s movement. Events are signals about app state, not ingredients for a user profile.
Sending telemetry does not block app use
Telemetry sending must not block app use. The event reporter stores events in a file queue and sends only a bounded batch at a time.
If sending fails, it waits and tries again later.
flowchart LR Event["App event"] --> Queue["Device file queue"] Queue --> Batch["Bounded batch"] Batch --> API["Telemetry endpoint"] API -->|Accepted| Metrics["Prometheus/DB metrics"] API -.->|Failure| Retry["Backoff retry"] Retry --> Queue
The reason for limiting count and size per send is simple. If telemetry makes the app slow, it has defeated its purpose.
I separated performance signals from behavior signals
App events and performance events are separate. Performance telemetry watches technical signals such as API path, status code, duration, and cache use. Looking at usage patterns and checking API stability are different goals.
On the server, Hangangjari records metrics such as accepted event count, rejection reasons, ingestion duration, and push acknowledgements. These numbers are signals for observing app health on dashboards, not tools for user profiling.
This separation also makes dashboards less risky. Finding a slow API and tracking a user’s movement pattern are completely different things. Hangangjari keeps only the minimum signals needed for the first question.
APNs device tokens and notification settings
Notifications are the exception where server-side storage is needed. APNs device tokens, interested parks/lots, notification thresholds, and quiet hours are required so the server can send notifications requested by users.
So the push area is handled more strictly.
- Store required values only when notifications are enabled.
- Limit APNs device-identifying values to delivery purpose.
- Clean up invalid device tokens and old subscriptions.
- Keep reason codes in notification rules and delivery audit to protect users.
Push means the server acts on a user’s behalf. So it has to explain “why did we send this, and why did we not send that?” before “can we send this?” Being able to explain that also helps collect less personal data.
Policy documents must match what the app does
The iOS Privacy Manifest and privacy policy are not explanations added later. They are promises to the App Store and users about what the app actually collects and does not collect.
In Hangangjari, the choices are:
- No accounts or login.
- No advertising-identifier based tracking.
- No server upload of location coordinates.
- App events center on anonymous hashes and minimal operational signals.
- APNs device tokens are for sending notifications.
- If public source data may include sensitive strings, limit them before storage/display.
Seeing less made the app easier to explain
Telemetry had to begin with “what will we not look at?” before “what will we look at?” Nearby sorting can be built without server-side location storage, and anonymous install hashes need limits around purpose and retention so they do not become accounts.
Separating app events from performance events made dashboard interpretation easier. The telemetry queue limits batch size and retries so it does not block app use.
The Privacy Manifest and privacy policy were aligned to say the same thing the app actually does.
In Hangangjari, privacy was not wording added at the end. Once I decided what data not to see, the analytics stayed inside that boundary. Seeing less did not make the app weaker. It became the standard for building an app that can be explained to users.
Share
No comments yet. You can leave the first one.
Pending review