{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreihs4mrlwbobzwy4lq6nylaz7bcehtmgx2wahuv5ngdy4v7gddddd4",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mom7jvsoqwq2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreign5bq2jfrhi2ks3txfajtcc3uob7dq3i33uvu3mqj77etylu2d2m"
    },
    "mimeType": "image/webp",
    "size": 451672
  },
  "path": "/hiyoyok/conflict-resolution-in-a-bidirectional-sync-app-how-i-handle-the-hard-cases-5e24",
  "publishedAt": "2026-06-19T01:07:18.000Z",
  "site": "https://dev.to",
  "tags": [
    "tauri",
    "rust",
    "android",
    "programming",
    "HiyokoAutoSync",
    "@hiyoyok"
  ],
  "textContent": "All tests run on an 8-year-old MacBook Air. All results from shipping 7 Mac apps as a solo developer. No sponsored opinion.\n\nHiyokoAutoSync does bidirectional sync between Android and Mac. Bidirectional sync has a hard problem: what happens when the same file is modified on both sides? Here's how I handle it.\n\n##  The conflict cases\n\n  1. File modified on both sides since last sync — which version wins?\n  2. File deleted on one side, modified on the other — delete or keep?\n  3. File moved on one side — move on the other side or treat as delete + create?\n\n\n\nMost sync apps punt on cases 1 and 3. Here's my approach.\n\n##  Detecting conflicts\n\nTrack last-synced state in SQLite:\n\n\n\n    CREATE TABLE sync_state (\n        file_path TEXT PRIMARY KEY,\n        mac_hash TEXT,\n        android_hash TEXT,\n        mac_modified INTEGER,\n        android_modified INTEGER,\n        last_synced INTEGER\n    );\n\n\nOn sync check:\n\n\n\n    fn classify_file(record: &SyncRecord, mac_stat: &FileStat, android_stat: &FileStat) -> SyncAction {\n        let mac_changed = mac_stat.hash != record.mac_hash;\n        let android_changed = android_stat.hash != record.android_hash;\n\n        match (mac_changed, android_changed) {\n            (true, false)  => SyncAction::CopyToAndroid,\n            (false, true)  => SyncAction::CopyToMac,\n            (false, false) => SyncAction::NoOp,\n            (true, true)   => SyncAction::Conflict,\n        }\n    }\n\n\n##  Conflict resolution strategies\n\nI offer three strategies, user-configurable:\n\n**Newer wins** : compare modification timestamps, keep the more recent file.\n\n\n\n    SyncAction::Conflict => {\n        if mac_stat.modified > android_stat.modified {\n            SyncAction::CopyToAndroid\n        } else {\n            SyncAction::CopyToMac\n        }\n    }\n\n\n**Mac always wins** : for users who treat Mac as source of truth.\n\n**Keep both** : rename one file with a conflict suffix, keep both versions.\n\n\n\n    // Rename Android version to \"file.conflict-2026-05-01.ext\"\n    let conflict_name = add_conflict_suffix(&file_path);\n    copy_to_mac_as(&android_file, &conflict_name)?;\n    copy_to_android(&mac_file)?;\n\n\n##  Delete vs modify conflict\n\nFile deleted on Mac, modified on Android:\n\n\n\n    (Deleted, Modified) => {\n        // Default: keep the modified file, restore it on Mac\n        // Alternative: delete from both sides\n        // User configurable\n        SyncAction::RestoreToMac\n    }\n\n\nThe safe default is to keep data. Deleting across both sides on a conflict can cause data loss users didn't intend.\n\n##  The verdict\n\nBidirectional sync without conflict resolution is a bug waiting to happen. The \"newer wins\" strategy covers 90% of real-world cases. Keep both covers the rest. Make it configurable for power users.\n\n**TL;DR:** Track sync state (hash + modified time) in SQLite per file. Classify each file into `CopyToAndroid`, `CopyToMac`, `NoOp`, or `Conflict` using a match on what changed. Offer three strategies: newer wins, Mac wins, or keep both. For delete vs modify conflicts, default to keeping data — accidental deletion is worse than a duplicate.\n\nIf this was useful, a ❤️ helps more than you'd think — thanks!\n\nHiyokoAutoSync | X → @hiyoyok",
  "title": "Conflict Resolution in a Bidirectional Sync App — How I Handle the Hard Cases"
}