# Privacy By Design

> Privacy-by-design principles on mobile — data minimization, purpose binding, consent. Use when introducing new data collection or third-party SDKs.

- Skill: `almasumdev/privacy-by-design` (Agent Skill)
- Install (CLI): `npx skillmds@latest add almasumdev/privacy-by-design`
- Raw SKILL.md: https://api.skillmd.com/api/skills/almasumdev/privacy-by-design/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: almasumdev (https://skillmd.com/u/almasumdev)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/almasumdev/privacy-by-design

---


# 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:

1. **What** exactly are we collecting (field list, types, precision)?
2. **Why** — which user-facing feature does it enable?
3. **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.

```swift
var config = PHPickerConfiguration(photoLibrary: .shared())
config.selectionLimit = 1
config.filter = .images
let picker = PHPickerViewController(configuration: config)
// No Photos permission prompt required for this path.
```

```kotlin
val launcher = registerForActivityResult(
    ActivityResultContracts.PickVisualMedia()
) { uri -> /* process one image; no READ_MEDIA_IMAGES needed */ }
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").

```kotlin
// Don't
Timber.d("User profile: $profile")
// Do
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](../../compliance/gdpr-mobile/SKILL.md).

## Checklist

- [ ] Every new field has a documented purpose and retention period.
- [ ] Location / photos / contacts use the least-privileged API (reduced accuracy, PhotoPicker, hashed lookup).
- [ ] No feature reuses data for a new purpose without fresh consent.
- [ ] Processing happens on-device where feasible.
- [ ] Identifiers used are the weakest that work; no deprecated hardware identifiers.
- [ ] Every third-party SDK is entered in the iOS Privacy Manifest and Play Data Safety form.
- [ ] Logs, crash reports, and analytics do not carry raw PII.
- [ ] Export and delete paths ship with the feature, not later.

