A SwiftPM package that lets developers declare their PII in Swift the way they do in Python — working on both server-side Swift (Vapor/Hummingbird, owns a Postgres DB) and Apple platforms (iOS/macOS apps) — without forking the GDPR engine into Swift.
Decided architecture (ideation): Swift DSL → manifest → one Python engine
Swift app / Vapor server → declares PII in Swift types
▼
effaced manifest (versioned JSON) ← language-neutral contract (#63)
▼
Python engine executes
• server: co-located service (sidecar) or, later, embedded via PythonKit
• iOS/macOS: calls the service over HTTP
Why not a native Swift engine port: under widened SemVer what gets deleted is the API; two engines that must delete identically forever is the worst maintenance risk. The manifest is already versioned + forward-migrated, so Swift only authors it.
Why "Both" splits in two: effaced erases transactionally against a DB the app owns (ADR 0006/0007). An iOS app owns no such DB, and embedding CPython on-device is heavy and pointless. So Apple = thin client; server = executes. One shared Swift declaration layer, two execution backends.
Layers (split into child issues if needed)
- Declaration DSL — Swift value types mirroring the manifest (
DataMap/TableEntry/ColumnEntry/PiiSpec/SubjectLink + PiiCategory/LegalBasis/ErasureStrategy); @resultBuilder so it feels like the Python annotation experience.
- Manifest encoder —
Codable lowering to the exact DataMap.to_payload() JSON (schema_version: 1).
GDPRClient — thin async URLSession client hitting the service endpoints (export/erase/consent + manifest register). All an Apple app needs.
- (Later, optional) in-process runner — PythonKit embedding effaced for single-binary server deploys. Deferred; the sidecar gives identical behaviour without CPython-bundling pain.
Key correctness mechanism — cross-language golden test
Swift-emitted manifest JSON must round-trip through Python DataMap.from_payload(...).to_payload() byte-equivalently (and vice-versa). A shared fixtures/manifest_v1.json checked by both a Swift test and a Python test keeps the schemas in lockstep forever.
Placement & CI
Outside the uv workspace, mirroring site/ (ADR 0011). Needs a Swift CI job (swift-actions/setup-swift, or the netcup ARM64 self-hosted runner).
Related: #63.
A SwiftPM package that lets developers declare their PII in Swift the way they do in Python — working on both server-side Swift (Vapor/Hummingbird, owns a Postgres DB) and Apple platforms (iOS/macOS apps) — without forking the GDPR engine into Swift.
Decided architecture (ideation): Swift DSL → manifest → one Python engine
Why not a native Swift engine port: under widened SemVer what gets deleted is the API; two engines that must delete identically forever is the worst maintenance risk. The manifest is already versioned + forward-migrated, so Swift only authors it.
Why "Both" splits in two: effaced erases transactionally against a DB the app owns (ADR 0006/0007). An iOS app owns no such DB, and embedding CPython on-device is heavy and pointless. So Apple = thin client; server = executes. One shared Swift declaration layer, two execution backends.
Layers (split into child issues if needed)
DataMap/TableEntry/ColumnEntry/PiiSpec/SubjectLink+PiiCategory/LegalBasis/ErasureStrategy);@resultBuilderso it feels like the Python annotation experience.Codablelowering to the exactDataMap.to_payload()JSON (schema_version: 1).GDPRClient— thinasyncURLSession client hitting the service endpoints (export/erase/consent + manifest register). All an Apple app needs.Key correctness mechanism — cross-language golden test
Swift-emitted manifest JSON must round-trip through Python
DataMap.from_payload(...).to_payload()byte-equivalently (and vice-versa). A sharedfixtures/manifest_v1.jsonchecked by both a Swift test and a Python test keeps the schemas in lockstep forever.Placement & CI
Outside the uv workspace, mirroring
site/(ADR 0011). Needs a Swift CI job (swift-actions/setup-swift, or the netcup ARM64 self-hosted runner).Related: #63.