-
Notifications
You must be signed in to change notification settings - Fork 116
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
Improve data fetching in Order Details, to avoid I/O performance on the main thread #14999
Conversation
…oved `feeLinesResultsController` and related logic
…ge, but not present inside the Networking model. Doing this to reduce number of `ResultsController` from `OrderDetailsResultsControllers`: - Update `Networking.generated.swift`, `Models+Copiable.generated.swift`, and `Order.swift` to include `shippingLabels` property in `Order` struct - Update `OrderDetailsResultsControllers.swift` by removing `shippingLabelResultsController` and using `order.shippingLabels` instead - Update various files to incorporate `shippingLabels` property in Order creation and manipulation - Add `Sendable` conformance to `ShippingLabel`, `ShippingLabelAddress`, `ShippingLabelRefund`, `ShippingLabelRefundStatus`, and `ShippingLabelStatus` structs and enums for concurrency safety - Update tests in `OrderDetailsDataSourceTests.swift` to handle `shippingLabels` within `Order` instances
…ch-fetching-in-order-details
|
Thanks for working on this, @pmusolino! Not directly related to this PR: While attempting to test this PR I noticed that the "Add Extension To Store" from the following screen doesn't work. I also noticed that the code is not linked to any action. Code link ![]() Video recording: Simulator.Screen.Recording.-.iPhone.15.Pro.Max.-.2025-01-29.at.11.13.48.mp4 |
I noticed one more issue that is probably not related to this PR. I have installed WooCommerce Shipping on my JN store, but I still see the "Get WooCommerce Shipping" banner in the Order Details screen. Simulator.Screen.Recording.-.iPhone.15.Pro.Max.-.2025-01-29.at.11.22.44.mp4I have tried restarting the app (stop and run again from Xcode), but the banner doesn't go away. |
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.
The following issue happens in trunk as well and I don't think this is related to this PR. IMO, this looks related to this CUW post - peaMlT-Yo-p2
After creating a shipping label on my JN store I started facing issues with loading orders.
I tried emptying the orders creating Woo Smooth Generator from my JN store and kept only the order that I manually created from the mobile app. But still the mobile app fails to load that order and shows the following decoding error in logs.
⛔️ Error synchronizing orders: typeMismatch(Swift.Int64, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 4", intValue: 4), CodingKeys(stringValue: "line_items", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "taxes", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "id", intValue: nil)], debugDescription: "Expected to decode Int64 but found a string instead.", underlyingError: nil))
My JN site has the following plugins installed.
Please let me know if you need an invitation to my JN site.
FWIW the "Get WooCommerce Shipping" banner with this install prompt is behind the |
@pmusolino While trying to Refund a shipping label, I found that the "Request refund" button doesn't do anything. |
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.
Quoting the following from the "Testing Information" in the PR description
- Ensure that any changes to the shipping labels (e.g., addition, refund) are immediately reflected in the UI without requiring a manual refresh.
I noticed that the order detail page doesn't reflect the newly purchased shipping label unless I pull down to refresh. Even after I pull to refresh, I see the "Create Shipping Label" button, and it goes away after another pull down to refresh.
Simulator.Screen.Recording.-.iPhone.15.Pro.Max.-.2025-01-30.at.12.13.22.mp4
I filled an issue here about removing the feature flag. |
This doesn't work in |
I tested it by creating the shipping label from the web, and it works; it's visible in the app when opening the order. I also tried creating the shipping label from the app. In this specific case, the refresh does not work as you mentioned, but it also doesn't refresh in the |
@@ -239,6 +245,9 @@ public struct Order: Decodable, Sendable, GeneratedCopiable, GeneratedFakeable { | |||
return OrderAttributionInfo(metaData: allOrderMetaData) | |||
}() | |||
|
|||
// Shipping labels | |||
let shippingLabels = try container.decodeIfPresent([ShippingLabel].self, forKey: .shippingLabels) ?? [] |
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.
👋 @pmusolino Do we receive this shipping_labels
key in orders response? Based on my testing in a Woo store with shipping labels, I don't see it being sent in wc/v3/orders
.
If we do receive the shipping_labels
, should we update the mock JSON and add unit tests to ensure that shipping_labels
are mapped correctly?
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.
Good question, @selanthiraiyan. In ShippingLabelStore
, the method upsertShippingLabels
connects the fetched shipping labels to the specific order, so the order doesn't return specific shipping labels, making it unnecessary to decode the Shipping Labels in the Order model. However, declaring the property (without decoding) in the networking model seems necessary to access shippingLabels
, unless you have another solution in mind.
Yes, we could access the shipping labels from storage, but that's what we're trying to avoid. Do you think I can leave the shippingLabels
property in Order.swift
but remove the decoding? This way, by default, it would be an empty array.
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.
Thanks, @pmusolino! I don't have a solution in mind. But I think we should remove the decoding, as it will be misleading.
I'm tagging @itsmeichigo to check whether she has any other solutions.
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.
Thanks for the ping Sharma!
I think if the key for shipping labels is non-existent in the order response, keeping the property an empty array should be the way to go. There's no need to waste more resources to look for such a key to decode. Please also leave a comment in the code to explain why the value is empty by default, and when it's going to be updated.
However, I'm concerned about this part:
In ShippingLabelStore, the method upsertShippingLabels connects the fetched shipping labels to the specific order
In the order details screen, after syncing shipping labels, we'd need to ensure that the order
in the view model is updated with the fetched label(s), just so they can be displayed on the UI.
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.
Thank you both! I updated the decoding here 85c0115 the comment should be clear enough.
In the order details screen, after syncing shipping labels, we'd need to ensure that the order in the view model is updated with the fetched label(s), just so they can be displayed on the UI.
Yes, this already happens from my testing. If you create or update a shipping label, or open an order detail with an existing shipping label that needs to be fetched remotely, you will see the order detail UI correctly updated.
Version |
…ch-fetching-in-order-details-take-2
…ch-fetching-in-order-details
@selanthiraiyan when I replied in that discussion with Huong, I wasn't aware of the problem mentioned here by Rachel. Why?
So, when you create a shipping label from the web without saving the order, the order's modification date does not update. As a result, everything does not refresh correctly because the shipping label and the order are treated as two separate entities. However, if you refresh the order, the shipping labels will be attached correctly. When we get the shipping labels from the syncShippingLabels() method, now we need to explicitly attach them to the order using:
Previously, this was unnecessary because we were retrieving data from storage. Let me know if I have cleared your doubts, Sharma. 🙌 |
Version |
Thanks for the detailed answer, @pmusolino! Would it work to sync the shipping labels first before loading the order from the storage?
In the above approach,
|
Version |
@selanthiraiyan Not sure this is the ideal solution because the core of Order Detail is the sync of the order in |
Thanks for the explanation, @pmusolino! I understand. Manually updating the order with the synched shipping labels seems like the way to go. Do you plan to add those changes to this PR? |
@selanthiraiyan if you think the work I did here feat/batch-fetching-in-order-details...feat/batch-fetching-in-order-details-take-2 works, I can merge the changes in this PR 🙌 |
@pmusolino Yes, please go ahead with this. Thanks! |
@selanthiraiyan done ✅ waiting for the final approval |
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.
Thanks for your patience with this work, Paolo! ❤️
Code
I left a few minor comments and one question about the code. We are getting pretty close to merging this PR. 👏
Testing
- I validated that a label purchased from web is shown in the order details page. This test case scenario. ✅
- The order details page doesn't reflect a newly purchased shipping label unless I pull down to refresh. Mentioned in this previous comment I tested that this bug exists in trunk as well. ✅
👋 @itsmeichigo It would be great if you could you take a look at this PR as a confidence check? Thanks! 🙇
group.leave() | ||
} | ||
// Check Woo Shipping support first, to ensure correct flows are enabled for shipping labels. | ||
self?.dataSource.isEligibleForWooShipping = ((await self?.isWooShippingSupported()) != nil) |
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.
We could unwrap self
using a guard statement to avoid using self as an optional here and a few other places below.
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.
Done in bbfb18c.
WooCommerce/Classes/ViewModels/Order Details/OrderDetailsViewModel.swift
Outdated
Show resolved
Hide resolved
self?.dataSource.isEligibleForShippingLabelCreation = isEligible ?? false | ||
|
||
// Sync shipping labels and update order with the result if available | ||
if let shippingLabels = await self?.syncShippingLabels() { |
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.
Should we call syncShippingLabels
even if checkShippingLabelCreationEligibility
returned false above? If yes, can we start this request asynchronously without waiting for the response from checkShippingLabelCreationEligibility
?
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.
We still need to sync shipping labels just in case the creation eligibility is only updated recently. I moved these calls to a task group to ensure they don't block each other in bbfb18c.
Thanks for the ping @selanthiraiyan! I don't have any additional comment aside from your latest ones. Regarding this point:
I did some debugging, and this seems to be an issue with I fixed this by switching to |
@selanthiraiyan I added the final changes following your latest comments, please take another look when you can. |
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.
Thanks for handling this, Huong! The changes LGTM.
Let's merge this PR. 🚀
Thank you, Huong, for taking care of the remaining changes! 🙏 |
Closes: #14923
Description
We observed that while opening product details, it's easy to see the following warning, generated by multiple reads on storage (Core Data). Also, this issue sometimes generate a lag opening the Order Detail screen.
This warning is occurring because we're making a lot of different fetch requests when loading the details, causing the hang issue.
I solved the issue by removing two fetches from
OrderDetailsResultsControllers
:feeLines
property to return the relationship fromorder.fees
instead, of using a Results Controller to fetch the order fees.shippingLabels
property toOrder
which was connected in Storage with a relationship, but not present inside the Networking model.Networking.generated.swift
,Models+Copiable.generated.swift
, andOrder.swift
to includeshippingLabels
property inOrder
struct.OrderDetailsResultsControllers.swift
by removingshippingLabelResultsController
and usingorder.shippingLabels
instead.shippingLabels
property in Order creation and manipulation.Sendable
conformance toShippingLabel
,ShippingLabelAddress
,ShippingLabelRefund
,ShippingLabelRefundStatus
, andShippingLabelStatus
structs and enums for concurrency safety.OrderDetailsDataSourceTests.swift
to handleshippingLabels
withinOrder
instances.Those were the only relationships with Order. In my opinion, we could potentially improve performance further by handling some fetches externally (e.g., fetching all order statuses). However, the current implementation is structured such that each view/model doesn't have visibility beyond the order itself. While this approach is possible, we need to discuss how deep we want to go with such changes. The current solution, however, does address the I/O problem effectively.
Steps to reproduce
Open multiple times different Order Details. You can notice a purple warning mentioning
Performing 1/0 on the main thread by reading or writing to a database can cause slow launches.
inResultsController
.Testing information
You should not be able anymore to replicate the warning after these changes.
- Ensure the "Create Shipping Label" button is visible when the order is eligible for shipping label creation and has no existing labels.
- Ensure the "Create Shipping Label" button is not visible when the order is eligible but already has shipping labels.
- Ensure the "More" button is visible in the product section when the order is eligible and has no refunded shipping labels.
- Ensure the "More" button is visible in the product section when the order is eligible and has refunded shipping labels.
- Ensure the "More" button is not visible in the product section when the order is not eligible for shipping label creation.
- Ensure that refunded shipping labels are correctly identified and handled, and that the UI reflects this state appropriately.
- Confirm that the order is correctly marked as eligible for shipping label creation based on predefined criteria (e.g., order status, payment status).
- Ensure that any changes to the shipping labels (e.g., addition, refund) are immediately reflected in the UI without requiring a manual refresh.
RELEASE-NOTES.txt
if necessary.Reviewer (or Author, in the case of optional code reviews):
Please make sure these conditions are met before approving the PR, or request changes if the PR needs improvement: