Upload Progress in Sync Tasks View#1452
Conversation
d689b5d to
1b4cc74
Compare
1b4cc74 to
3bb33d6
Compare
There was a problem hiding this comment.
Pull request overview
Adds upload progress reporting to the queued sync tasks UI by plumbing upload progress values from notifications into the task list and rendering them per task.
Changes:
- Track upload progress updates in
SyncJobSchedulerand pass a progress dictionary into task retrieval. - Extend
SyncTaskReferencewith aprogressfield and populate it inSyncTasksStorage. - Update profile queued tasks UI to show a circular progress indicator for upload tasks and live-update it via notifications.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| Shared/Services/Sync/SyncTasksStorage.swift | Adds progress parameter to getAllTasks and maps it into SyncTaskReference. |
| Shared/Services/Sync/SyncJobScheduler.swift | Stores per-relativePath progress and passes it when fetching queued jobs; clears progress on completion/cancel. |
| Shared/Services/Sync/Model/SyncTask.swift | Extends SyncTaskReference to include progress. |
| BookPlayer/Profile/Profile/QueuedSyncTasksView.swift | Passes progress/isUpload into row view for upload tasks. |
| BookPlayer/Profile/Profile/QueuedSyncTaskRowView.swift | Displays circular progress and updates it from .uploadProgressUpdated notifications. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| /// Reference for observer | ||
| private var syncTasksObserver: NSKeyValueObservation? | ||
| private var progressObservation: NSKeyValueObservation? |
There was a problem hiding this comment.
progressObservation is introduced but never used anywhere in this type. Removing it avoids confusion and prevents dead code from accumulating.
| private var progressObservation: NSKeyValueObservation? |
| .onAppear { | ||
| self.progress = self.initialProgress | ||
| } |
There was a problem hiding this comment.
progress is initialized to 0.0 and only updated from initialProgress in onAppear, which can briefly render the row without progress and can get out of sync if the view stays mounted while initialProgress changes (e.g., list reload). Prefer initializing the @State from initialProgress in a custom init (e.g., _progress = State(initialValue: initialProgress)) and/or add onChange(of: initialProgress) to keep state consistent.
| .onReceive( | ||
| NotificationCenter.default.publisher(for: .uploadProgressUpdated) | ||
| .receive(on: DispatchQueue.main) | ||
| ) { notification in |
There was a problem hiding this comment.
Upload progress notifications can arrive very frequently (URLSession may report many updates per second), and this onReceive will trigger a SwiftUI re-render each time. Consider throttling/debouncing the .uploadProgressUpdated publisher before updating @State (similar to the throttling used for .folderProgressUpdated elsewhere) to reduce UI churn.
| for path: String, | ||
| value: Double | ||
| ) { | ||
| tasksProgress[path] = value |
There was a problem hiding this comment.
tasksProgress[path] = value mutates shared state without any synchronization, but tasksProgress is also accessed from other executors/queues. To avoid races, perform this write on the same serialization mechanism you use for reads (e.g., lockQueue.async), or isolate tasksProgress in an actor/@mainactor context.
| tasksProgress[path] = value | |
| lockQueue.async { [weak self] in | |
| self?.tasksProgress[path] = value | |
| } |
| public func getAllQueuedJobs() async -> [SyncTaskReference] { | ||
| _ = await initializeStoreTask?.result | ||
| return await taskStore.getAllTasks() | ||
| return await taskStore.getAllTasks(progress: tasksProgress) | ||
| } |
There was a problem hiding this comment.
tasksProgress is read here while it’s also being mutated from other queues/executors (main queue in the .uploadProgressUpdated sink, lockQueue in handleFinishedTask, etc.). This is a real data race risk. Consider taking a thread-safe snapshot (e.g., via lockQueue.sync { tasksProgress }) or moving tasksProgress behind an actor/@mainactor so reads and writes share the same executor.
Purpose
uploadApproach
Things to be aware of / Things to focus on
Screenshots