DEV Community

Sebastien Lato
Sebastien Lato

Posted on

SwiftUI Soft Deletes & Data Lifecycle Policies (Designing for Recovery, Compliance & Trust)

Most apps delete data like this:

context.delete(item)
Enter fullscreen mode Exit fullscreen mode

That works…

until you need:

  • undo after app relaunch
  • sync reconciliation
  • audit trails
  • regulatory compliance (GDPR, retention policies)
  • multi-device recovery
  • accidental deletion protection
  • staged data removal

At that point, hard deletes become one of the most dangerous operations in your app.

This post shows how to design soft deletes and data lifecycle policies in SwiftUI that are:

  • recoverable
  • compliant
  • sync-safe
  • user-friendly
  • production-grade

🧠 The Core Principle

Deletion is a state β€” not an action.

Data should transition through lifecycle stages, not disappear instantly.


🧱 1. Hard Delete vs Soft Delete

Hard delete:

database.remove(item)
Enter fullscreen mode Exit fullscreen mode

Soft delete:

item.deletedAt = Date()
Enter fullscreen mode Exit fullscreen mode

Soft delete preserves:

  • history
  • undo capability
  • sync correctness

🧬 2. Model Deletion as State

struct Todo: Identifiable, Codable {
    let id: UUID
    var title: String
    var deletedAt: Date?
}
Enter fullscreen mode Exit fullscreen mode

Active item:

deletedAt == nil
Enter fullscreen mode Exit fullscreen mode

Deleted item:

deletedAt != nil
Enter fullscreen mode Exit fullscreen mode

Deletion becomes a state transition.


πŸ” 3. Why Soft Deletes Matter in Sync Systems

Without soft deletes:

  • deletion may not sync correctly
  • remote devices may resurrect items
  • conflicts become ambiguous

With soft deletes:

  • deletion propagates like any update
  • reconciliation is deterministic
  • restores are possible

🧱 4. Querying Active Data

Never filter in UI.

Centralize logic:

func activeTodos() -> [Todo] {
    todos.filter { $0.deletedAt == nil }
}
Enter fullscreen mode Exit fullscreen mode

Prevents accidental display of deleted items.


πŸ”„ 5. Undo & Restore Flows

Soft delete enables recovery:

func restore(_ item: inout Todo) {
    item.deletedAt = nil
}
Enter fullscreen mode Exit fullscreen mode

This allows:

  • undo after crash
  • multi-device restore
  • user trust

βš–οΈ 6. Data Retention Policies

Not all deleted data should live forever.

Example lifecycle:

Active β†’ Soft Deleted β†’ Archived β†’ Permanently Deleted
Enter fullscreen mode Exit fullscreen mode

Model:

enum DataLifecycleState {
    case active
    case deleted(Date)
    case archived(Date)
}
Enter fullscreen mode Exit fullscreen mode

This enables:

  • retention compliance
  • storage optimization
  • legal requirements

🌐 7. Soft Deletes & Multi-Device Sync

Scenario:

  • Device A deletes item
  • Device B edits item offline

With soft deletes:

  • conflict is detectable
  • resolution policy is clear

Without soft deletes:

  • item may reappear
  • state becomes inconsistent

πŸ§ͺ 8. Testing Deletion Safety

Test scenarios:

  • delete β†’ restore β†’ sync
  • delete on one device, edit on another
  • migration with deleted data
  • background sync of deletions
  • retention policy execution

Deletion bugs destroy trust.


⚠️ 9. Common Anti-Patterns

Avoid:

  • immediate hard deletes
  • filtering deleted items only in UI
  • losing deletion timestamps
  • not syncing deletion state
  • permanent deletion without retention window

These lead to:

  • ghost data
  • data loss
  • inconsistent sync
  • compliance risks

🧠 Mental Model

Think:

Active
 β†’ Soft Deleted
   β†’ Archived
     β†’ Purged
Enter fullscreen mode Exit fullscreen mode

Not:

β€œDelete and forget.”


πŸš€ Final Thoughts

Soft deletes and lifecycle policies give you:

  • safer user experience
  • reliable sync
  • compliance readiness
  • recovery from mistakes
  • long-term data trust

This is the difference between:

  • fragile deletion
  • and responsible data stewardship

Top comments (0)