-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Create BGContinuedProcessingTask to upload media
#24891
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Conversation
| <string>spectrum-'22-icon-app-83.5x83.5</string> | ||
| <string>spectrum-'22-icon-app-60x60</string> | ||
| <string>spectrum-'22-icon-app-76x76</string> | ||
| <string>spectrum-'22-icon-app-83.5x83.5</string> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Xcode made these changes. Xcode also made the original changes. Somehow, Xcode decides to revert changes that were made by itself...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What version of Xcode are you on?
|
| App Name | WordPress | |
| Configuration | Release-Alpha | |
| Build Number | 29655 | |
| Version | PR #24891 | |
| Bundle ID | org.wordpress.alpha | |
| Commit | 1ce20c0 | |
| Installation URL | 0uf9jjmkolktg |
|
| App Name | Jetpack | |
| Configuration | Release-Alpha | |
| Build Number | 29655 | |
| Version | PR #24891 | |
| Bundle ID | com.jetpack.alpha | |
| Commit | 1ce20c0 | |
| Installation URL | 15cpksdkb4hfo |
jkmassel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm glad we're doing this, but I'm concerned about how many errors we're ignoring.
Any IO error shouldn't be ignored, otherwise we end up with impossible-to-debug issues.
I'm also curious what happens if a user schedules an image for upload then immediately deletes it before it starts uploading (or while it's in progress)
|
Version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks pretty neat! LGTM, but disclaimer I'm not familiar with this API.
I left one main comment about actor vs MainActor and I'd also consider monitoring using MediaCoordinator (latest example).
In terms of features, I'm not sure it should be shown for quick operations like uploading a site image or a featured image for a post – it may be too overwhelming, and other apps don't do this. I think it should be reserved only for actually long background operations like uploading large videos or multiple files.
| <string>spectrum-'22-icon-app-83.5x83.5</string> | ||
| <string>spectrum-'22-icon-app-60x60</string> | ||
| <string>spectrum-'22-icon-app-76x76</string> | ||
| <string>spectrum-'22-icon-app-83.5x83.5</string> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What version of Xcode are you on?
|
|
||
| @available(iOS 26.0, *) | ||
| /// Utilize `BGContinuedProcessingTask` to show the uploading media activity. | ||
| private actor ConcreteMediaUploadBackgroundTracker: MediaUploadBackgroundTracker { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mixing actor isolation with @MainActor functions (updateMessaging, updateResult) adds complexity. I would suggest to synchronize ConcreteMediaUploadBackgroundTracker itself on the main thread as it doesn't perform any work and potentially creates more CPU overhead by adding context switches and by re-reading stuff from the Core Data. MediaCoordinator, PostMediaUploadsViewModel, etc are also all synchronized on main also for simplicity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest to synchronize ConcreteMediaUploadBackgroundTracker itself on the main thread
I'd prefer not to use the main thread since the Background Task API does not require it. I used the @MainActor on those functions to simplify Core Data query code. But I can have a look to see if it's possible to synchronize the Core Data calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to stick with the main context because of how the media uploading is implemented. But I have removed the @MainActor from the function declaration, and moved it to the limited scopes of the Core Data queries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, would it be better to synchronize ConcreteMediaUploadBackgroundTracker on the main thread? There are disadvantages to making it an actor. This relatively small class currently requires 13 await calls that could all be eliminated.
| private var state: BGTaskState = .idle | ||
|
|
||
| private init?() { | ||
| let taskId = (Bundle.main.infoDictionary?["BGTaskSchedulerPermittedIdentifiers"] as? [String])?.first { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nit) I'd suggest making init non-optional to remove the need for the code using this class to deal with the optional.
| } | ||
|
|
||
| let success = mediaItems.allSatisfy { $0.remoteStatus == .sync } | ||
| await setTaskCompleted(success: success) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(not) this is the kind of complexity – one function with three concurrent code sections – I mentioned earlier that could be easily eliminated by synchronizing the class on main. You may end up code sections from updateMessaging and updateResult being intertwined in any order, so it's hard to reason about.
@jkmassel Do you mean the I didn't add code to observe Core Data changes to immediately handle deleting uploading images, because the Core Data notifications get fired pretty frequently. That's one issue I noticed in the UI, where the uploading status is not updated immediately when deleting uploading images. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approved, although I wasn't able to verify if there aren't any logic race conditions in the coordinator. I strongly suggest simply putting it on the main actor, as making it a standalone actor doesn't seem to achieve anything but adds a significant amount of complexity. I opened a prototype PR just to show how much simpler it is if it's on main – it eliminates 12 await s: #24943.
| private var coreDataChangesObserver: NSObjectProtocol? | ||
|
|
||
| private init() { | ||
| let taskId = Bundle.main.bundleIdentifier! + ".mediaUpload" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove the face unwrapping just in case.
@kean I had a go myself and noticed that it's very easy to make the mistake of calling MainActor bound functions from background threads. Take the I spent quite a while trying to avoid that issue: declaring the type as
I agree that we should avoid jumping between actors if we can, especially in this particular case, where we have to use the MainActor frequently. However, fewer |
If there is some pre-concurrency code, which I assume
It is what
It's easier to follow code that has less concurrency. If it's possible to eliminate 12 async calls in a ~100 lines of code, it's a big difference. If I needed to debug and fix something in |
|
Maybe using a practical example can help illustrate my concerns. Let's use this one single happy path as the test method: Upload an image from the Media screen, the OS shows an "Uploading Media" banner, updates the progress, and finally dismisses the banner once uploaded. Obviously, the test works on this PR branch. I have refactored the code to make everything run on the main thread in the The test works on the If you check out the diff, you'll see there is no My concern is that Xcode does not warn us about removing That brings us to this point: if you reset your working copy to the If we have the complete strict concurrency checks turned on in the app target, it's a different story. I'd expect the compiler to warn us about the code changes of removing |
Yes, I agree. Readability is important. I'm happy to push the changes in |
d072677 to
153eb2b
Compare
@kean @jkmassel Regarding this, I feel like it's too much to show the banner immediately after selecting images to upload, too. In the latest commit, I have changed to only show the banner when the app goes to the background. That means we can get extra background time for the uploads, at the same time, we don't confuse the user with the new banner when the app is active. Let me know what you think. |
Sure, it is designed to work with Swift Concurrency. If you have legacy APIs with closures that do not enforce actor isolation, you will be able to call syncronous methods on a MainActor-isolated class without warnings. You have to check it yourself and add runtime checks. The reason I suggested (as a nit and after approval) to isolated it on main mainly to reduce the number of async calls to make the code easier to follow, and not necessarily for the compile-time checks. I understand the appeal of actors that they guarantee isolation, but, to me, it doesn't seem like this advantage is bigger than the disadvantages of making so much of the code concurrent and harder to follow. It's pretty easy to ensure the few places where
Nothing beats the good old "just synchronize everything on main thread" 😃 Btw, not so well-known fact about |
|
@kean I have merged the |
|





Description
Here is a recording.
ScreenRecording_09-26-2025.22-53-19_1.MP4