Skip to content

Commit 0bebc05

Browse files
authored
Add option to fetch lightweight objects to reduce app hangs (#15774)
2 parents b7e7ffd + c65ddb4 commit 0bebc05

File tree

12 files changed

+179
-9
lines changed

12 files changed

+179
-9
lines changed

Modules/Sources/Yosemite/Model/Storage/Order+ReadOnlyConvertible.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,51 @@ extension Storage.Order: ReadOnlyConvertible {
123123

124124
}
125125

126+
/// Returns a lightweight ReadOnly version of the receiver without any relationships populated.
127+
///
128+
public func toLightweightReadOnly() -> Yosemite.Order {
129+
Order(siteID: siteID,
130+
orderID: orderID,
131+
parentID: parentID,
132+
customerID: customerID,
133+
orderKey: orderKey,
134+
isEditable: isEditable,
135+
needsPayment: needsPayment,
136+
needsProcessing: needsPayment,
137+
number: number ?? "",
138+
status: OrderStatusEnum(rawValue: statusKey),
139+
currency: currency ?? "",
140+
currencySymbol: "", // Not stored in the Storage Layer, only used in the notifications extension.
141+
customerNote: customerNote ?? "",
142+
dateCreated: dateCreated ?? Date(),
143+
dateModified: dateModified ?? Date(),
144+
datePaid: datePaid,
145+
discountTotal: discountTotal ?? "",
146+
discountTax: discountTax ?? "",
147+
shippingTotal: shippingTotal ?? "",
148+
shippingTax: shippingTax ?? "",
149+
total: total ?? "",
150+
totalTax: totalTax ?? "",
151+
paymentMethodID: paymentMethodID ?? "",
152+
paymentMethodTitle: paymentMethodTitle ?? "",
153+
paymentURL: paymentURL as URL?,
154+
chargeID: chargeID,
155+
items: [],
156+
billingAddress: createReadOnlyBillingAddress(),
157+
shippingAddress: createReadOnlyShippingAddress(),
158+
shippingLines: [],
159+
coupons: [],
160+
refunds: [],
161+
fees: [],
162+
taxes: [],
163+
customFields: [],
164+
renewalSubscriptionID: renewalSubscriptionID,
165+
appliedGiftCards: [],
166+
attributionInfo: attributionInfo?.toReadOnly(),
167+
shippingLabels: [])
168+
169+
}
170+
126171

127172
// MARK: - Private Helpers
128173

Modules/Sources/Yosemite/Model/Storage/Product+ReadOnlyConvertible.swift

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,93 @@ extension Storage.Product: ReadOnlyConvertible {
200200
customFields: productCustomFields.sorted { $0.metadataID < $1.metadataID })
201201
}
202202

203+
/// Returns a lightweight ReadOnly version of the receiver.
204+
/// No relationships are populated except for images.
205+
///
206+
public func toLightweightReadOnly() -> Yosemite.Product {
207+
208+
let productImages = imagesArray.map { $0.toReadOnly() }
209+
210+
return Product(siteID: siteID,
211+
productID: productID,
212+
name: name,
213+
slug: slug,
214+
permalink: permalink,
215+
date: date ?? Date(timeIntervalSince1970: 0),
216+
dateCreated: dateCreated ?? Date(timeIntervalSince1970: 0),
217+
dateModified: dateModified,
218+
dateOnSaleStart: dateOnSaleStart,
219+
dateOnSaleEnd: dateOnSaleEnd,
220+
productTypeKey: productTypeKey,
221+
statusKey: statusKey,
222+
featured: featured,
223+
catalogVisibilityKey: catalogVisibilityKey,
224+
fullDescription: fullDescription,
225+
shortDescription: briefDescription,
226+
sku: sku,
227+
globalUniqueID: globalUniqueID,
228+
price: price,
229+
regularPrice: regularPrice,
230+
salePrice: salePrice,
231+
onSale: onSale,
232+
purchasable: purchasable,
233+
totalSales: Int(totalSales),
234+
virtual: virtual,
235+
downloadable: downloadable,
236+
downloads: [],
237+
downloadLimit: downloadLimit,
238+
downloadExpiry: downloadExpiry,
239+
buttonText: buttonText,
240+
externalURL: externalURL,
241+
taxStatusKey: taxStatusKey,
242+
taxClass: taxClass,
243+
manageStock: manageStock,
244+
stockQuantity: nil,
245+
stockStatusKey: stockStatusKey,
246+
backordersKey: backordersKey,
247+
backordersAllowed: backordersAllowed,
248+
backordered: backordered,
249+
soldIndividually: soldIndividually,
250+
weight: weight,
251+
dimensions: createReadOnlyDimensions(),
252+
shippingRequired: shippingRequired,
253+
shippingTaxable: shippingTaxable,
254+
shippingClass: shippingClass,
255+
shippingClassID: shippingClassID,
256+
productShippingClass: nil,
257+
reviewsAllowed: reviewsAllowed,
258+
averageRating: averageRating,
259+
ratingCount: Int(ratingCount),
260+
relatedIDs: convertIDArray(relatedIDs),
261+
upsellIDs: convertIDArray(upsellIDs),
262+
crossSellIDs: convertIDArray(crossSellIDs),
263+
parentID: parentID,
264+
purchaseNote: purchaseNote,
265+
categories: [],
266+
tags: [],
267+
images: productImages,
268+
attributes: [],
269+
defaultAttributes: [],
270+
variations: variations ?? [],
271+
groupedProducts: groupedProducts ?? [],
272+
menuOrder: Int(menuOrder),
273+
addOns: [],
274+
isSampleItem: isSampleItem,
275+
bundleStockStatus: nil,
276+
bundleStockQuantity: bundleStockQuantity as? Int64,
277+
bundleMinSize: bundleMinSize?.decimalValue,
278+
bundleMaxSize: bundleMaxSize?.decimalValue,
279+
bundledItems: [],
280+
password: password,
281+
compositeComponents: [],
282+
subscription: subscription?.toReadOnly(),
283+
minAllowedQuantity: minAllowedQuantity,
284+
maxAllowedQuantity: maxAllowedQuantity,
285+
groupOfQuantity: groupOfQuantity,
286+
combineVariationQuantities: combineVariationQuantities?.boolValue,
287+
customFields: [])
288+
}
289+
203290
// MARK: - Private Helpers
204291

205292
private func createReadOnlyDimensions() -> Yosemite.ProductDimensions {

Modules/Sources/Yosemite/SnapshotsProvider/FetchResultSnapshotsProvider.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,16 @@ public final class FetchResultSnapshotsProvider<MutableType: FetchResultSnapshot
179179
return nil
180180
}
181181
}
182+
183+
/// The lightweight version of `object(withID:)` method. Returns readonly objects without relationships.
184+
///
185+
public func lightweightObject(withID objectID: FetchResultSnapshotObjectID) -> MutableType.ReadOnlyType? {
186+
if let storageOrder = storage.loadObject(ofType: MutableType.self, with: objectID) {
187+
return storageOrder.toLightweightReadOnly()
188+
} else {
189+
return nil
190+
}
191+
}
182192
}
183193

184194
// MARK: - FetchedResultsController Activation

Modules/Sources/Yosemite/Tools/ReadOnlyConvertible.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ public protocol ReadOnlyConvertible: TypeErasedReadOnlyConvertible {
1717
/// Returns a ReadOnly version of the receiver.
1818
///
1919
func toReadOnly() -> ReadOnlyType
20+
21+
/// Returns a lightweight ReadOnly version of the receiver.
22+
///
23+
func toLightweightReadOnly() -> ReadOnlyType
2024
}
2125

2226

@@ -40,4 +44,10 @@ extension ReadOnlyConvertible {
4044
public func toTypeErasedReadOnly() -> Any {
4145
return toReadOnly()
4246
}
47+
48+
/// Default implementation is the same result with `toReadOnly()`
49+
///
50+
public func toLightweightReadOnly() -> ReadOnlyType {
51+
toReadOnly()
52+
}
4353
}

Modules/Sources/Yosemite/Tools/ResultsController.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ public class ResultsController<T: ResultsControllerMutableType> {
143143
return controller.object(at: indexPath).toReadOnly()
144144
}
145145

146+
/// Returns the lightweight fetched object at a given indexPath.
147+
///
148+
public func lightweightObject(at indexPath: IndexPath) -> T.ReadOnlyType {
149+
return controller.object(at: indexPath).toLightweightReadOnly()
150+
}
151+
146152
/// Returns the fetched object at the given `indexPath`. Returns `nil` if the `indexPath`
147153
/// does not exist.
148154
///
@@ -203,6 +209,17 @@ public class ResultsController<T: ResultsControllerMutableType> {
203209
return readOnlyObjects ?? []
204210
}
205211

212+
/// Returns an array of all of the (ReadOnly) Fetched Objects with no relationships populated.
213+
/// Suitable for list views.
214+
///
215+
public var lightweightFetchedObjects: [T.ReadOnlyType] {
216+
let readOnlyObjects = controller.fetchedObjects?.compactMap { mutableObject in
217+
mutableObject.toLightweightReadOnly()
218+
}
219+
220+
return readOnlyObjects ?? []
221+
}
222+
206223
/// Returns an array of SectionInfo Entitites.
207224
///
208225
public var sections: [SectionInfo] {

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
-----
66
- [*] Order Details: Fix crash when reloading data [https://github.com/woocommerce/woocommerce-ios/pull/15764]
77
- [*] Shipping Labels: Improved shipment management UI by hiding remove/merge options instead of disabling them, hiding merge option for orders with 2 or fewer unfulfilled shipments, and hiding the ellipsis menu when no remove/merge actions are available [https://github.com/woocommerce/woocommerce-ios/pull/15760]
8+
- [*] Improve app performance by fetching lightweight objects when necessary [https://github.com/woocommerce/woocommerce-ios/pull/15774]
89
- [**] POS: a POS tab in the tab bar is now available in the app for stores eligible for Point of Sale, instead of the previous entry point in the Menu tab. [https://github.com/woocommerce/woocommerce-ios/pull/15766]
910
- [***] POS: Barcode scanning using Bluetooth scanners in Point of Sale (HID-based scanners are supported) [https://github.com/woocommerce/woocommerce-ios/pull/15776]
1011
- [*] POS: Support refreshing product list when it's empty [https://github.com/woocommerce/woocommerce-ios/pull/15782]

WooCommerce/Classes/ViewModels/Order Details/OrderDetailsResultsControllers.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ final class OrderDetailsResultsControllers {
9090
/// Products from an Order
9191
///
9292
var products: [Product] {
93-
return productResultsController.fetchedObjects
93+
return productResultsController.lightweightFetchedObjects
9494
}
9595

9696
/// ProductVariations from an Order

WooCommerce/Classes/ViewRelated/Blaze/CampaignCreation/BlazeCampaignCreationFormViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ final class BlazeCampaignCreationFormViewModel: ObservableObject {
204204

205205
private let storage: StorageManagerType
206206
private var product: Product? {
207-
guard let product = productsResultsController.fetchedObjects.first else {
207+
guard let product = productsResultsController.lightweightFetchedObjects.first else {
208208
assertionFailure("Unable to fetch product with ID: \(productID)")
209209
return nil
210210
}

WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ private extension DashboardViewModel {
540540
return
541541
}
542542

543-
guard ordersResultsController.fetchedObjects.isEmpty else {
543+
guard ordersResultsController.lightweightFetchedObjects.isEmpty else {
544544
hasOrders = true
545545
return
546546
}

WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Items Section/WooShippingItemsDataSource.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ final class DefaultWooShippingItemsDataSource: WooShippingItemsDataSource {
3232
///
3333
private var products: [Product] {
3434
try? productResultsController.performFetch()
35-
return productResultsController.fetchedObjects
35+
return productResultsController.lightweightFetchedObjects
3636
}
3737

3838
/// Stored product variations that match the items in the order.

0 commit comments

Comments
 (0)