Skip to content
VoteFirst Docs
Dashboard

Swift

Privacy

What the Swift package collects, what it stores, what it sends, and what App Store Connect wants you to tick.

The manifest

VoteFirstKit ships a PrivacyInfo.xcprivacy. It declares two collected data types and nothing else.

Data typeWhat it is
User IDThe anonymous voter identifier. Linked to the user. Not used for tracking. App functionality
Other user contentThe suggestions and comments people write. Linked to the user. Not used for tracking. App functionality
TrackingFalse
Tracking domainsNone
Required reason APIsNone

Both are marked linked to the user because the voter identifier is what a vote is recorded against, and it is what makes you already voted for this possible. Neither is marked for tracking, because nothing here follows anybody across other companies' apps or websites.

There are unit tests over the manifest itself: that tracking is false, that the required reason list is empty, that the two types are declared honestly, and that the file reaches the bundle under its own name. If somebody changes the manifest without meaning to, the tests say so.

What to tick in App Store Connect

Under App Privacy, unless your app already declares them:

  • Identifiers → User ID, linked to the user, not used for tracking.
  • User Content → Other User Content, linked to the user, not used for tracking.

Both for App Functionality.

That is the whole of what this package adds. If your app already collects a user id, the existing declaration covers it.

What is stored on the device

WhatWhereWhy
The anonymous voter tokenThe keychain, under com.votefirst.sdkSo votes and comments survive a relaunch
The same token, when the keychain refusesApplication Support/VoteFirst/{project}/voter.tokenSo an unsigned debug build keeps its voter. 0600, complete file protection
Writes waiting for a connectionApplication Support/VoteFirst/{project}/outbox.jsonSo a vote made on a train lands when the train arrives
Which stages this reader turned onApplication Support/VoteFirst/{project}/view.jsonSo a board looks the same next time
What this reader reported or blockedApplication Support/VoteFirst/{project}/hidden.jsonSo they do not see it again
Cached responsesCaches/VoteFirst/So a board that has not changed costs a round trip and no payload

Nothing is written to user defaults, on purpose. See Required reason APIs.

What leaves the device

Every request goes to your VoteFirst host and nowhere else. There is no analytics endpoint, no crash reporter and no third party in the path.

HeadersThe embed key, the voter token, an idempotency key on a write, and a correlation id
Read bodiesNone. Reads are GETs
Write bodiesExactly what the person typed: a heading and a description, or a comment
AutomaticallyNothing. The package makes no request you did not cause by showing a screen or making a call

The server sees the request's network address, the way every server does. The package sends no device identifier, no advertising identifier, no model, no OS version, and no name of your app.

What the package never touches

  • No IDFA, no ASIdentifierManager, no App Tracking Transparency prompt.
  • No contacts, photos, location, camera, microphone, calendar or health data.
  • No UserDefaults, no NSUbiquitousKeyValueStore, no iCloud sync.
  • No background tasks, no push notification registration, no silent wake ups.
  • No dependency of any kind, so nothing else is in your binary because of this.

Required reason APIs

The list in the manifest is empty, and that is a design decision rather than an omission.

Reading user defaults is a required reason API. A package that keeps an identifier there obliges every app that carries it to declare that API in their own manifest, which is somebody else's paperwork created by our convenience. The keychain and the application support directory have no such requirement, so that is where the token and the reader's own lists live.

File timestamps, disk space and system boot time are the other three in that category, and nothing here reads any of them.

Resetting somebody

In your app
await VoteFirst.forget()

Drops this device's voter, the cached identity and anything queued to send. The next write mints a new voter with no history.

It does not clear what the reader chose to hide, because choosing not to see abuse again is not part of an identity.

It is a local reset. Votes and comments already recorded stay on the board under the voter that made them, and removing those is a request to the project owner, who can delete a voter and everything they wrote from the dashboard.

What is yours to write

The package declares what it collects. Your app's own privacy policy still has to say that your users' suggestions and comments are sent to a service you use, because that is your disclosure to make and not ours.

Apple also asks an app carrying content other people wrote to publish terms, offer a way to report content, offer a way to block a person, and moderate what is filed. The package brings the last three: reporting and blocking are on every comment and every feature, and every suggestion waits for the owner to approve it. The terms are yours, and there is a place to hang them.

In your app
VoteFirst.config.termsURL = URL(string: "https://example.com/terms")
VoteFirst.config.privacyURL = URL(string: "https://example.com/privacy")

Next