Skip to content

Commit f174086

Browse files
committed
Improve uploading status reporting
1 parent d5785da commit f174086

1 file changed

Lines changed: 95 additions & 18 deletions

File tree

WordPress/Classes/ViewRelated/Aztec/Media/MediaUploadBackgroundTracker.swift

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func mediaUploadBackgroundTracker() -> MediaUploadBackgroundTracker? {
2222
/// Utilize `BGContinuedProcessingTask` to show the uploading media activity.
2323
private actor ConcreteMediaUploadBackgroundTracker: MediaUploadBackgroundTracker {
2424
struct Item {
25+
// Please note: all media query needs to be done in the main context, due to the current upload media implementation.
2526
var media: TaggedManagedObjectID<Media>
2627
var progress: Progress
2728
}
@@ -55,6 +56,8 @@ private actor ConcreteMediaUploadBackgroundTracker: MediaUploadBackgroundTracker
5556
// State transtion: idle -> pending -> accepted -> [accepted...] -> idle.
5657
private var state: BGTaskState = .idle
5758

59+
private var coreDataChangesObserver: NSObjectProtocol?
60+
5861
private init() {
5962
let taskId = Bundle.main.bundleIdentifier! + ".mediaUpload"
6063

@@ -74,9 +77,12 @@ private actor ConcreteMediaUploadBackgroundTracker: MediaUploadBackgroundTracker
7477
await self?.taskCreated(task)
7578
}
7679
}
80+
7781
}
7882

7983
func track(progress: Progress, media: TaggedManagedObjectID<Media>) async {
84+
observeCoreDataChanges()
85+
8086
let item = Item(media: media, progress: progress)
8187
switch state {
8288
case .idle:
@@ -101,6 +107,31 @@ private actor ConcreteMediaUploadBackgroundTracker: MediaUploadBackgroundTracker
101107
}
102108
}
103109

110+
private func observeCoreDataChanges() {
111+
guard coreDataChangesObserver == nil else { return }
112+
113+
coreDataChangesObserver = NotificationCenter.default.addObserver(
114+
forName: .NSManagedObjectContextObjectsDidChange,
115+
object: ContextManager.shared.mainContext,
116+
queue: .main
117+
) { [weak self] notification in
118+
let deleted = notification.userInfo?[NSManagedObjectContext.NotificationKey.deletedObjects.rawValue] as? Set<NSManagedObject> ?? []
119+
120+
var mediaObjectIDs = Set<TaggedManagedObjectID<Media>>()
121+
for object in deleted {
122+
if let media = object as? Media {
123+
mediaObjectIDs.insert(TaggedManagedObjectID(media))
124+
}
125+
}
126+
127+
if !mediaObjectIDs.isEmpty {
128+
Task {
129+
await self?.handleMediaObjectsUpdates(updated: mediaObjectIDs)
130+
}
131+
}
132+
}
133+
}
134+
104135
private func taskCreated(_ task: BGContinuedProcessingTask) {
105136
task.progress.totalUnitCount = 100
106137
task.expirationHandler = { [weak self] in
@@ -164,32 +195,51 @@ private actor ConcreteMediaUploadBackgroundTracker: MediaUploadBackgroundTracker
164195
setTaskCompleted(success: false)
165196
}
166197

167-
private func handleProgressUpdates() {
198+
private func handleProgressUpdates() async {
168199
guard case let .accepted(accepted) = state else { return }
169200

170-
let fractionCompleted = accepted.items.map(\.progress.fractionCompleted).reduce(0, +) / Double(accepted.items.count)
201+
let progresses = await MainActor.run {
202+
let context = ContextManager.shared.mainContext
203+
return accepted.items
204+
.filter { item in
205+
(try? context.existingObject(with: item.media)) != nil
206+
}
207+
.map(\.progress)
208+
}
209+
210+
let fractionCompleted = progresses.map(\.fractionCompleted).reduce(0, +) / Double(progresses.count)
171211
accepted.task.progress.completedUnitCount = Int64(fractionCompleted * Double(accepted.task.progress.totalUnitCount))
172212
}
173213

214+
private func handleMediaObjectsUpdates(updated: Set<TaggedManagedObjectID<Media>>) async {
215+
guard case let .accepted(accepted) = state else { return }
216+
217+
let needsUpdate = accepted.items.contains(where: { updated.contains($0.media) })
218+
if needsUpdate {
219+
await handleStatusUpdates()
220+
}
221+
}
222+
174223
private func handleStatusUpdates() async {
175224
await updateMessaging()
176225
await updateResult()
177226
}
178227

179-
@MainActor
180228
private func updateMessaging() async {
181-
guard case let .accepted(accepted) = await self.state else { return }
229+
guard case let .accepted(accepted) = self.state else { return }
182230

183-
let context = ContextManager.shared.mainContext
184-
let mediaItems = accepted.items.compactMap { try? context.existingObject(with: $0.media) }
231+
let statuses = await MainActor.run {
232+
let context = ContextManager.shared.mainContext
233+
return accepted.items.compactMap { try? context.existingObject(with: $0.media).uploadStatus }
234+
}
185235

186-
let failed = mediaItems.count { $0.remoteStatus == .failed }
187-
let success = mediaItems.count { $0.remoteStatus == .sync }
188-
let total = mediaItems.count
236+
let failed = statuses.count { $0 == .failure }
237+
let success = statuses.count { $0 == .success}
238+
let uploading = statuses.count { $0 == .uploading }
189239

190240
var subtitle = [String]()
191-
if total - success - failed > 0 {
192-
subtitle.append(String.localizedStringWithFormat(Strings.uploadingStatus, total - success - failed))
241+
if uploading > 0 {
242+
subtitle.append(String.localizedStringWithFormat(Strings.uploadingStatus, uploading))
193243
}
194244
if success > 0 {
195245
subtitle.append(String.localizedStringWithFormat(Strings.successStatus, success))
@@ -201,20 +251,21 @@ private actor ConcreteMediaUploadBackgroundTracker: MediaUploadBackgroundTracker
201251
accepted.task.updateTitle(Strings.uploadingMediaTitle, subtitle: ListFormatter.localizedString(byJoining: subtitle))
202252
}
203253

204-
@MainActor
205254
private func updateResult() async {
206-
guard case let .accepted(accepted) = await self.state else { return }
255+
guard case let .accepted(accepted) = self.state else { return }
207256

208-
let context = ContextManager.shared.mainContext
209-
let mediaItems = accepted.items.compactMap { try? context.existingObject(with: $0.media) }
257+
let mediaStatuses = await MainActor.run {
258+
let context = ContextManager.shared.mainContext
259+
return accepted.items.compactMap { try? context.existingObject(with: $0.media).uploadStatus }
260+
}
210261

211-
let completed = mediaItems.allSatisfy { $0.remoteStatus == .sync || $0.remoteStatus == .failed }
262+
let completed = mediaStatuses.allSatisfy { $0 == .success || $0 == .failure }
212263
guard completed else {
213264
return
214265
}
215266

216-
let success = mediaItems.allSatisfy { $0.remoteStatus == .sync }
217-
await setTaskCompleted(success: success)
267+
let success = mediaStatuses.allSatisfy { $0 == .success }
268+
setTaskCompleted(success: success)
218269
}
219270

220271
private func setTaskCompleted(success: Bool) {
@@ -226,6 +277,32 @@ private actor ConcreteMediaUploadBackgroundTracker: MediaUploadBackgroundTracker
226277
}
227278
}
228279

280+
private enum MediaUploadStatus: Hashable {
281+
case success
282+
case failure
283+
case uploading
284+
case unknown
285+
}
286+
287+
private extension Media {
288+
var uploadStatus: MediaUploadStatus {
289+
if let number = remoteStatusNumber, let status = MediaRemoteStatus(rawValue: number.uintValue) {
290+
switch status {
291+
case .sync:
292+
return .success
293+
case .failed:
294+
return .failure
295+
case .pushing, .processing:
296+
return .uploading
297+
default:
298+
return .unknown
299+
}
300+
} else {
301+
return .unknown
302+
}
303+
}
304+
}
305+
229306
private enum Strings {
230307
static let uploadingMediaTitle = NSLocalizedString(
231308
"BGTask.mediaUpload.title",

0 commit comments

Comments
 (0)