Skip to content

Conversation

@jaclync
Copy link
Contributor

@jaclync jaclync commented Jun 13, 2025

Fixes WOOMOB-607

Just one reviewer is required.

Why

In most of the network calls except for the enqueue<M: Mapper>(_ request: Request, mapper: M) async throws -> M.Output version, the response is parsed by a mapper on the main thread. When the response size is large like in the support case in WOOMOB-607 with a large amount of product metadata, the UI could become unresponsive. This PR just updates the ProductsRemote.loadAllProducts network call from the previous Combine/completion block based enqueue method to the enqueue<M: Mapper>(_ request: Request, mapper: M) async throws method, so that the response parsing along with other network related tasks can be performed in the background thread. There are already several use cases of enqueue<M: Mapper>(_ request: Request, mapper: M) async throws for a while that we can consider it stable.

I'm prioritizing network calls for entities with potentially large amount of data like from metadata or some collection fields. Each change like this takes 200+ diffs, so I'm making changes one at a time.

Description

This pull request refactors several methods in the ProductsRemote class and its related components to adopt Swift's async/await syntax, improving readability, simplifying asynchronous code, and for the response parsing to be in a background thread. It also updates corresponding unit tests and mock implementations to align with these changes.

Refactoring to async/await in ProductsRemote:

  • Networking/Sources/Extended/Remote/ProductsRemote.swift: Replaced the completion handler in loadAllProducts with async/await and updated the method signature to return [Product]. The enqueue method now directly returns the result instead of using a completion handler. It also adopts the generic ListMapper so that we can change limit the response size for more use cases in WOOMOB-608. [1] [2] [3]

Updates to dependent classes and methods:

Unit test updates:

Mock implementation updates:

Additional test refactor:

Steps to reproduce

Prerequisite: put a breakpoint in ListMapper decode calls in Xcode

  • Launch the app and wait for the breakpoint in the prerequisite triggered by ProductStore.synchronizeProducts --> it should be in a background thread
  • Remove breakpoint and continue with the app
  • Go to the Products tab --> the products should be loaded correctly as before

Screenshots

Screenshot 2025-06-13 at 4 32 25 PM
  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

…ethod for response parsing to be in a background thread.
@jaclync jaclync added this to the 22.7 milestone Jun 13, 2025
@jaclync jaclync added type: task An internally driven task. feature: product list Related to the product list. labels Jun 13, 2025
@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Jun 13, 2025

App Icon📲 You can test the changes from this Pull Request in WooCommerce iOS Prototype by scanning the QR code below to install the corresponding build.

App NameWooCommerce iOS Prototype
Build Number30431
VersionPR #15743
Bundle IDcom.automattic.alpha.woocommerce
Commit40268f3
Installation URL2ma8oc046dego
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@jaclync jaclync marked this pull request as ready for review June 13, 2025 08:37
@iamgabrielma iamgabrielma self-assigned this Jun 13, 2025
Copy link
Contributor

@iamgabrielma iamgabrielma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Just a non-blocker comment/question in one of the tasks

excludedProductIDs: excludedProductIDs,
shouldDeleteStoredProductsOnFirstPage: shouldDeleteStoredProductsOnFirstPage,
onCompletion: onCompletion)
Task { @MainActor in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one of my test runs I did encounter a concurrency warning on ProductAttribute+ReadOnlyConvertible but I have not been able to reproduce it again. From my initial check, it seemed to come from here so I've tried to play around with removing the call to @MainActor in this task, but I saw no difference

[...]
/woocommerce-ios/Yosemite/Yosemite/Model/Storage/ProductAttribute+ReadOnlyConvertible.swift:24 Performing I/O on the main thread can cause hangs.
This is known to cause hangs for your users.

Since this sync calls both the mapper via loadAllProducts and later upsertStoredProductsInBackground in background threads, perhaps we are able to remove the MainActor tag if not needed 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In one of my test runs I did encounter a concurrency warning on ProductAttribute+ReadOnlyConvertible but I have not been able to reproduce it again.

Did the stack trace start from accessing ResultsController.fetchedObjects? I've been seeing ProductAttribute+ReadOnlyConvertible warnings in trunk and the PR branch for a while. I think it's from Product's relationships being loaded on the main thread (they were probably not loaded in storage before that) when converting the storage object to readonly struct. And there are several relationships of the Product model, all of them can have any number of related objects. I don't see an issue yet, will create one.

Screenshot 2025-06-16 at 9 53 26 PM Screenshot 2025-06-16 at 9 54 07 PM Screenshot 2025-06-16 at 9 56 54 PM

Since this sync calls both the mapper via loadAllProducts and later upsertStoredProductsInBackground in background threads, perhaps we are able to remove the MainActor tag if not needed 🤔

The @MainActor only puts the non-awaiting code on the main thread, in this case the completion callbacks. I think this is expected and how it works before, this PR just ensures the networking code, particularly the response decoding part, is not on the main thread.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue about the performance warning: WOOMOB-619

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did the stack trace start from accessing ResultsController.fetchedObjects? I've been seeing ProductAttribute+ReadOnlyConvertible warnings in trunk and the PR branch for a while.

I'm not sure, but now that you mention it I might have seen the same warning outside this PR as well before 🤔

Thanks for logging it in!

The @mainactor only puts the non-awaiting code on the main thread, in this case the completion callbacks. I think this is expected and how it works before, this PR just ensures the networking code, particularly the response decoding part, is not on the main thread.

👍

@jaclync jaclync merged commit 8317f7c into trunk Jun 17, 2025
13 checks passed
@jaclync jaclync deleted the backlog/WOOMOB-607-update-ProductsRemote.loadAllProducts-to-async-await branch June 17, 2025 06:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature: product list Related to the product list. type: task An internally driven task.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants