SwiftUI Idempotency & Duplicate Prevention (Correctness in Distributed Systems)

Most apps assume actions happen once:

submitOrder()

That works…

until you introduce:

  • retries after network failure
  • background sync
  • offline queues
  • app relaunch recovery
  • migration reprocessing
  • slow server responses
  • user double taps

At that point, duplicate operations become one of the most dangerous bugs in your app.

This post shows how to design idempotent systems in SwiftUI that are:

  • safe to retry
  • duplicate-proof
  • crash-resilient
  • sync-friendly
  • production-grade

🧠 The Core Principle

Retrying an operation must not change the result.

If running an action twice creates a different outcome, your system is fragile.

🧱 1. What Is Idempotency?

An operation is idempotent if running it multiple times has the same effect as running it once.

Safe:

deleteItem(id)

Running twice → item is still deleted.

Unsafe:

createOrder()

Running twice → two orders created.

⚠️ 2. Why Mobile Apps Need Idempotency

Mobile environments guarantee retries:

  • network timeouts trigger retries
  • background tasks may run twice
  • app relaunch replays queued operations
  • sync engines retry failed operations
  • users double tap buttons

Without idempotency, you get:

  • duplicate charges
  • duplicated messages
  • corrupted state
  • inconsistent sync

🧬 3. Attach an Operation ID to Every Mutation

Every mutation must have a stable identity.

struct OperationID: Hashable, Codable {
    let value: UUID
}

Example operation:

struct CreateOrderOperation {
    let id: OperationID
    let payload: OrderPayload
}

The ID travels through:

  • local queue
  • network request
  • server processing
  • reconciliation

🌐 4. Server-Side Idempotency Keys

Send the operation ID with requests:

POST /orders
Idempotency-Key: 8F6C-1234-ABCD

Server logic:

  • if key is new → process
  • if key exists → return previous result

This guarantees safe retries.

📦 5. Local Deduplication Layer

Prevent duplicate execution locally.

final class OperationDeduplicator {
    private var processed: Set<OperationID> = []

    func shouldProcess(_ id: OperationID) -> Bool {
        processed.insert(id).inserted
    }
}

Usage:

guard deduplicator.shouldProcess(op.id) else { return }

Persist processed IDs for crash safety.

🔁 6. Idempotency in Sync Queues

With idempotency:

  • retries are safe
  • crash recovery is safe
  • migrations can replay operations
  • background sync won’t duplicate

Without it, retries corrupt data.

🧱 7. Designing Idempotent APIs

Prefer operations that include identity.

Bad:

POST /cart/items

Better:

PUT /cart/items/{itemID}

PUT is idempotent by design.

🔄 8. Handling Partial Failures

Network failure after server success is common.

Without idempotency:

  • retry creates duplicate

With idempotency:

  • retry returns existing result

Your system becomes self-healing.

⚠️ 9. Common Anti-Patterns

Avoid:

  • relying on timestamps for uniqueness
  • generating IDs on retry
  • storing processed IDs only in memory
  • assuming requests run once
  • not propagating IDs to server

These lead to:

  • duplicate charges
  • duplicated records
  • unrecoverable inconsistencies

🧪 10. Testing Idempotency

Test scenarios:

  • retry same operation 10×
  • crash before response received
  • replay queue after migration
  • simulate slow server responses
  • double-tap user actions

If your system survives these, it’s robust.

🧠 Mental Model

Think:

User Action
 → Operation ID
   → Persistent Queue
     → Retry
       → Server Deduplication
         → Consistent Result

Not:

“This request will only run once.”

🚀 Final Thoughts

Idempotency gives you:

  • safe retries
  • reliable sync
  • crash resilience
  • duplicate prevention
  • consistent distributed systems

This is the difference between:

  • a fragile mobile client
  • and a production-grade system
Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

Delivery Planning In IT & Software Development: Making a Delivery Plan

Related Posts