| name | privacy-by-design |
| description | Privacy-by-design principles on mobile — data minimization, purpose binding, consent. Use when introducing new data collection or third-party SDKs. |
Privacy by Design
Instructions
Privacy by Design is an engineering posture, not a legal afterthought. Decisions made in the first week of a feature outweigh months of compliance paperwork.
1. The Three Questions
Before adding any data collection, answer in the PR description:
- What exactly are we collecting (field list, types, precision)?
- Why — which user-facing feature does it enable?
- For how long — retention and deletion policy.
If you can't answer cleanly, don't add it.
2. Data Minimization
Collect the least-precise data that still makes the feature work:
- Location: need it only for "find nearest store"? Use
CLLocationManager at reduced accuracy (desiredAccuracy = kCLLocationAccuracyKilometer) or Android's PRIORITY_BALANCED_POWER_ACCURACY. Consider iOS 14+ approximate location (requestWhenInUseAuthorization + user choice).
- Contacts: need a phone number only to check membership? Hash client-side, ping server with prefix-truncated hashes (k-anonymity) — don't upload the full contact book.
- Photos: use the PhotoPicker (
PHPickerViewController on iOS, ActivityResultContracts.PickVisualMedia on Android) — you get the chosen asset without full library access.
var config = PHPickerConfiguration(photoLibrary: .shared())
config.selectionLimit = 1
config.filter = .images
let picker = PHPickerViewController(configuration: config)
val launcher = registerForActivityResult(
ActivityResultContracts.PickVisualMedia()
) { uri -> }
launcher.launch(
PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
)
3. Purpose Binding
- Each data field has one documented purpose.
- Reuse for a new purpose requires a new consent event, not a silent expansion.
- Analytics events that "might be useful later" are a red flag; collect when you have a concrete use.
4. On-Device Over Off-Device
- Perform processing on-device when feasible (ML classification, thumbnailing, redaction).
- Ship only derived / aggregate signals off-device.
- Apple's App Store policy, Google's Play Data Safety, and GDPR all reward this architecturally.
5. Identifiers
| Identifier | Use with caution |
|---|
| IDFA / GAID | Advertising only; needs ATT on iOS, UMP consent on EEA Android. |
ASIdentifierManager / advertising ID | Same as above. |
UIDevice.identifierForVendor / Settings.Secure.ANDROID_ID | Scoped identifiers; still PII under GDPR. |
| Server-issued user ID | Prefer this for analytics / crash correlation. |
| IMEI / MAC / serial | Do not use on modern OSes; restricted and often unavailable. |
Use the weakest identifier that works. Generate a random install ID scoped to your app (UUID.randomUUID() stored in Keychain / Keystore) for things like A/B bucketing.
6. Third-Party SDKs
Each SDK added is a data-sharing decision:
- Enumerate what it sends (many SDKs send IP, install ID, app events by default).
- Add to Apple's Privacy Manifest (
PrivacyInfo.xcprivacy) and Google Play's Data Safety form.
- Prefer SDKs that support delayed initialization until after consent.
- Audit at least yearly — SDKs expand scope between releases.
7. Logs, Crash Reports, Analytics
These are the three places PII leaks most often.
- Never log raw request / response bodies with user data in release builds.
- Strip PII from crash reports (stack traces are fine; user input in a
toString() is not).
- For analytics, log categories ("filter_applied") not content ("searched for: alice@example.com").
Timber.d("User profile: $profile")
Timber.d("Loaded profile uid=${profile.id.hashPrefix(8)}")
8. Default Settings
- Telemetry / crash reporting: opt-in in regulated regions, clearly disclosed elsewhere.
- Personalization / recommendations that rely on behavior: opt-in.
- Background location, microphone, camera: never "always" by default.
9. Data Export and Deletion
Shipping a feature → shipping an export path and a delete path. Build them alongside, not after launch. See gdpr-mobile.
Checklist