diff --git a/Virtusize/Sources/Models/VirtusizeServerProduct.swift b/Virtusize/Sources/Models/VirtusizeServerProduct.swift index cb0cec8c..dce87e22 100644 --- a/Virtusize/Sources/Models/VirtusizeServerProduct.swift +++ b/Virtusize/Sources/Models/VirtusizeServerProduct.swift @@ -115,11 +115,15 @@ public class VirtusizeServerProduct: Codable { /// Gets the InPage recommendation text based on the user and store product info func getRecommendationText( - _ i18nLocalization: VirtusizeI18nLocalization, + _ i18nLocalization: VirtusizeI18nLocalization?, _ sizeComparisonRecommendedSize: SizeComparisonRecommendedSize?, _ bodyProfileRecommendedSizeName: String?, _ trimType: VirtusizeI18nLocalization.TrimType = VirtusizeI18nLocalization.TrimType.ONELINE ) -> String { + guard let i18nLocalization = i18nLocalization else { + return Localization.shared.localize("inpage_default_accessory_text") + } + var text = i18nLocalization.getBodyDataEmptyText() if isAccessory() { text = accessoryText(i18nLocalization, sizeComparisonRecommendedSize) diff --git a/Virtusize/Sources/UI/VirtusizeInPageMini.swift b/Virtusize/Sources/UI/VirtusizeInPageMini.swift index a92c701d..be33e8fa 100644 --- a/Virtusize/Sources/UI/VirtusizeInPageMini.swift +++ b/Virtusize/Sources/UI/VirtusizeInPageMini.swift @@ -86,7 +86,7 @@ public class VirtusizeInPageMini: VirtusizeInPageView { inPageMiniMessageLabel.attributedText = NSAttributedString( string: sizeRecData.serverProduct.getRecommendationText( - VirtusizeRepository.shared.i18nLocalization!, + VirtusizeRepository.shared.i18nLocalization, sizeComparisonRecommendedSize, bodyProfileRecommendedSize?.getSizeName, VirtusizeI18nLocalization.TrimType.ONELINE diff --git a/Virtusize/Sources/Virtusize.swift b/Virtusize/Sources/Virtusize.swift index 2f362512..d197c711 100644 --- a/Virtusize/Sources/Virtusize.swift +++ b/Virtusize/Sources/Virtusize.swift @@ -63,7 +63,9 @@ public class Virtusize { internal static let virtusizeEventHandler = DefaultEventHandler() - internal static let dispatchQueue = DispatchQueue(label: "com.virtusize.default-queue") + /// Task to track current product load operation for cancellation + private static var loadProductTask: Task? + private static let loadProductTaskLock = NSLock() internal typealias SizeRecommendationData = ( // swiftlint:disable:this large_tuple serverProduct: VirtusizeServerProduct, @@ -71,16 +73,10 @@ public class Virtusize { bodyProfileRecommendedSize: BodyProfileRecommendedSize? ) - /// The private property for updating the size recommendation data for InPage views - private static var _sizeRecData: SizeRecommendationData? /// The property to be set to updating the size recommendation data for InPage views. internal static var sizeRecData: SizeRecommendationData? { - get { - return _sizeRecData - } - set { - _sizeRecData = newValue - if let sizeRecData = _sizeRecData { + didSet { + if let sizeRecData = sizeRecData { DispatchQueue.main.async { NotificationCenter.default.post( name: .sizeRecommendationData, @@ -94,16 +90,10 @@ public class Virtusize { internal typealias InPageError = (hasError: Bool, externalProductId: String) - /// The property to be set to show the InPage error screen with the associated external product ID - private static var _inPageError: InPageError? /// The property to be set to show the InPage error screen with the associated external product ID internal static var inPageError: InPageError? { - get { - return _inPageError - } - set { - _inPageError = newValue - if let inPageError = _inPageError { + didSet { + if let inPageError = inPageError { DispatchQueue.main.async { NotificationCenter.default.post( name: .inPageError, @@ -119,15 +109,26 @@ public class Virtusize { // MARK: - Methods /// A function for clients to populate the Virtusize views by loading a product public class func load(product: VirtusizeProduct) { - Task { + // Cancel previous load task if it exists + loadProductTaskLock.lock() + loadProductTask?.cancel() + + // Create new task + let task = Task { + // Check if cancelled early + guard !Task.isCancelled else { return } + let productWithPDCData = await virtusizeRepository.checkProductValidity(product: product) + guard !Task.isCancelled else { return } guard let productWithPDCData = productWithPDCData else { inPageError = (true, product.externalId) return } await virtusizeRepository.updateUserSession() + + guard !Task.isCancelled else { return } await MainActor.run { NotificationCenter.default.post( name: .productCheckData, @@ -141,6 +142,7 @@ public class Virtusize { productId: productWithPDCData.productCheckData?.productDataId ) + guard !Task.isCancelled else { return } guard let serverProduct = serverProduct else { inPageError = (true, product.externalId) return @@ -154,9 +156,17 @@ public class Virtusize { ) } + guard !Task.isCancelled else { return } await virtusizeRepository.fetchDataForInPageRecommendation(storeProduct: serverProduct) - virtusizeRepository.updateInPageRecommendation(product: serverProduct) + + guard !Task.isCancelled else { return } + await MainActor.run { + virtusizeRepository.updateInPageRecommendation(product: serverProduct) + } } + + loadProductTask = task + loadProductTaskLock.unlock() } /// Sets up the VirtusizeView and adds it to `virtusizeViews` diff --git a/Virtusize/Sources/VirtusizeRepository.swift b/Virtusize/Sources/VirtusizeRepository.swift index 742bff74..21a9565d 100644 --- a/Virtusize/Sources/VirtusizeRepository.swift +++ b/Virtusize/Sources/VirtusizeRepository.swift @@ -45,44 +45,16 @@ internal class VirtusizeRepository: NSObject { // swiftlint:disable:this type_bo private var bodyProfileRecommendedSize: BodyProfileRecommendedSize? private var hasSessionBodyMeasurement: Bool = false - private var _store: VirtusizeStore? - private let storeLock = NSLock() - private var store: VirtusizeStore? { - get { - storeLock.lock() - defer { storeLock.unlock() } - return _store - } - set { - storeLock.lock() - defer { storeLock.unlock() } - _store = newValue - } - } + private var store: VirtusizeStore? private var shouldReloadStoreI18n: Bool = true /// A set to cache the store product information of all the visited products - private var _serverStoreProductSet: Set = [] - private let productSetLock = NSLock() - - internal var serverStoreProductSet: Set { - get { - productSetLock.lock() - defer { productSetLock.unlock() } - return _serverStoreProductSet - } - set { - productSetLock.lock() - defer { productSetLock.unlock() } - _serverStoreProductSet = newValue - } - } + /// Note: No lock needed - protected by task cancellation in Virtusize.load() + internal var serverStoreProductSet: Set = [] - /// Thread-safe insert into serverStoreProductSet + /// Insert into serverStoreProductSet private func insertIntoProductSet(_ product: VirtusizeServerProduct) { - productSetLock.lock() - defer { productSetLock.unlock() } - _serverStoreProductSet.insert(product) + serverStoreProductSet.insert(product) } /// Checks if the product in `VirtusizeProduct` is valid and post notifications