Most production bugs are not crashes.
They are bad data:
- impossible states
- partial writes
- silent corruption
- invalid assumptions
- “this should never happen”
And once bad data enters your system, it spreads everywhere.
This post shows how to design data integrity & validation pipelines in SwiftUI that:
- prevent corruption
- enforce correctness
- surface issues early
- stay testable
- scale with complexity
🧠 The Core Principle
Data must be validated at every boundary.
If data crosses a boundary without validation, corruption is inevitable.
🧱 1. Identify Trust Boundaries
Every app has trust boundaries:
- network → app
- persistence → memory
- user input → model
- feature → feature
- version → version
Never assume data is valid across boundaries.
🧬 2. Validation Is a Pipeline, Not a Check
Bad:
if value.isValid { save(value) }
Correct:
RawInput
→ Sanitization
→ Validation
→ Normalization
→ Domain Model
Each step has a clear responsibility.
📦 3. Separate Raw Models from Domain Models
struct UserDTO {
let email: String?
let age: Int?
}
struct User {
let email: Email
let age: Age
}
Domain models cannot represent invalid states.
🔐 4. Typed Validation Objects
struct Email {
let value: String
init?(_ raw: String) {
guard raw.contains("@") else { return nil }
self.value = raw
}
}
If it exists, it’s valid — no rechecking required.
🧭 5. Central Validation Layer
Never validate in views.
protocol Validator {
func validate(_ input: RawInput) throws -> DomainModel
}
This allows:
- reuse
- testing
- consistent rules
- versioned validation
🧪 6. Validation Errors Are First-Class
enum ValidationError: Error {
case missingField(String)
case invalidFormat(String)
case outOfRange(String)
}
Never use generic errors.
Validation errors:
- inform UX
- inform analytics
- inform monitoring
🧠 7. Persistence Validation
Before saving:
func save(_ model: DomainModel) throws {
try invariantCheck(model)
persist(model)
}
Never trust in-memory state blindly.
🔁 8. Versioned Validation
Validation rules change over time.
enum ValidationVersion {
case v1
case v2
}
This allows:
- backward compatibility
- safe migrations
- gradual tightening of rules
⚠️ 9. Detect & Surface Corruption
When corruption is detected:
- log it
- capture context
- isolate the data
- fail safely
Silent corruption is worse than crashes.
❌ 10. Common Data Integrity Anti-Patterns
Avoid:
- optional-everything models
- validation in views
- trusting backend blindly
- skipping validation for “internal” data
- mixing validation with UI
- ignoring edge cases
Bad data compounds.
🧠 Mental Model
Think:
Input
→ Validate
→ Normalize
→ Persist
→ Trust
Not:
“It looks fine”
🚀 Final Thoughts
Data integrity architecture gives you:
- fewer bugs
- safer migrations
- predictable behavior
- easier debugging
- long-term stability
Security protects access.
Integrity protects truth.
Top comments (0)