Parent PRD
#110
Depends on
#125 (offline write queue), #121 (delta API endpoints with modifiedAfter)
Summary
Implement SyncService — triggered on App.OnResume and Connectivity.ConnectivityChanged (online transition). Push queued local changes to the API, then pull any remote changes since last sync. Apply last-write-wins conflict resolution using DateModified.
SyncService — push phase
- Query all local records where
IsPendingSync = true
- PATCH/POST each to the API
- On success, clear
IsPendingSync = false
- Soft-deleted records pushed as deletes, then removed locally on success
SyncService — pull phase
- Call
GET /api/posts?modifiedAfter={LastSyncTimestamp}
- Call
GET /api/notes?modifiedAfter={LastSyncTimestamp}
- For each returned record, compare
DateModified against local copy
- Keep whichever has the later
DateModified (last-write-wins)
- Update
LastSyncTimestamp = DateTime.UtcNow in Preferences after successful sync
Trigger points
App.OnResume (foreground sync when resuming app)
Connectivity.ConnectivityChanged event when transitioning to online
Tests (unit — mock dependencies)
- Push phase: sends all
IsPendingSync=true records; clears flag on success
- Pull phase: remote newer → remote wins; local newer → local wins
LastSyncTimestamp updated after successful full sync
- Use xUnit + FluentAssertions + Moq (already in codebase)
Acceptance Criteria
Stories from PRD
User stories 7, 8, 17, 18, 19
Parent PRD
#110
Depends on
#125 (offline write queue), #121 (delta API endpoints with modifiedAfter)
Summary
Implement
SyncService— triggered onApp.OnResumeandConnectivity.ConnectivityChanged(online transition). Push queued local changes to the API, then pull any remote changes since last sync. Apply last-write-wins conflict resolution usingDateModified.SyncService — push phase
IsPendingSync = trueIsPendingSync = falseSyncService — pull phase
GET /api/posts?modifiedAfter={LastSyncTimestamp}GET /api/notes?modifiedAfter={LastSyncTimestamp}DateModifiedagainst local copyDateModified(last-write-wins)LastSyncTimestamp = DateTime.UtcNowinPreferencesafter successful syncTrigger points
App.OnResume(foreground sync when resuming app)Connectivity.ConnectivityChangedevent when transitioning to onlineTests (unit — mock dependencies)
IsPendingSync=truerecords; clears flag on successLastSyncTimestampupdated after successful full syncAcceptance Criteria
SyncServiceimplemented with push and pull phasesLastSyncTimestamppersisted inPreferencesStories from PRD
User stories 7, 8, 17, 18, 19