From 4cbf15a3a75e250c033e7abcd7150da276f8fa86 Mon Sep 17 00:00:00 2001 From: "Hedden, Kyle Matthew" Date: Wed, 3 Jul 2024 15:42:12 -0400 Subject: [PATCH] Digital PCT265 defect CXTDT-579050: Change batching to the UI. --- .../BaseControllers/ViewController.swift | 63 ++++++++++++------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index e67188a0..950b330e 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -70,41 +70,34 @@ import Combine observingForResponses = NotificationCenter.default.publisher(for: NSNotification.Name(rawValue: NotificationResponseLoaded)) .receive(on: self.pageUpdateQueue) // Background serial queue. .map { [weak self] notification in - guard let self = self else { return (nil, nil, nil) } - // Get the page data. - let pageUpdates = self.extractInterestedPageType(from: notification.userInfo?.optionalDictionaryForKey(KeyPageMap) ?? [:]) - // Convert the page data into a new model. - var pageModel: PageModelProtocol? = nil - if let pageUpdates { - do { - // TODO: Rewiring to parse from plain JSON rather than this protocol indirection. - pageModel = try (self as? any TemplateProtocol & PageBehaviorHandlerProtocol & MVMCoreViewControllerProtocol)?.parseTemplate(pageJSON: pageUpdates) - } catch { - if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: self.pageType))") { - MVMCoreLoggingHandler.shared()?.addError(toLog: coreError) - } - } - } - // Get the module data. - let moduleUpdates = self.extractInterestedModules(from: notification.userInfo?.optionalDictionaryForKey(KeyModuleMap) ?? [:]) - // Bundle the transformations. - return (pageUpdates, pageModel, moduleUpdates) + self?.pullUpdates(from: notification) ?? (nil, nil, nil) } .filter { (pageUpdates: [String : Any]?, pageModel: PageModelProtocol?, moduleUpdates: [String : Any]?) in // Skip any non-updates. (pageUpdates != nil && pageModel != nil) || (moduleUpdates != nil && moduleUpdates!.count > 0) } - // Opportunity: Merge all module only updates into one event. + // Merge all page and module updates into one update event. + //.print("[Update pipe] merging") + .scan((nil, nil, nil)) { accumulator, next in + // Always take the latest page and the latest modules with same key. + return (next.0, next.1, next.2?.mergingRight(accumulator.2 ?? [:])) + } + //.print("[Update pipe] into buffer") + // Hold onto the latest merged state until UI is ready for an update. Keep only the latest from scan. + .buffer(size: 1, prefetch: .byRequest, whenFull: .dropOldest) + //.print("[Update pipe] out of buffer") // Delay allowing the previous model update to settle before triggering a re-render. - .buffer(size: 100, prefetch: .byRequest, whenFull: .dropOldest) - .flatMap(maxPublishers: .max(1)) { Just($0).delay(for: .seconds(0.1), scheduler: RunLoop.main) } + .flatMap(maxPublishers: .max(1)) { buffer in + Just(buffer).delay(for: .seconds(0.1), scheduler: RunLoop.main) + } .sink { [weak self] (pageUpdates: [String : Any]?, pageModel: PageModelProtocol?, moduleUpdates: [String : Any]?) in guard let self = self else { return } - if let pageUpdates, let pageModel { + if let pageUpdates, pageModel != nil { self.loadObject?.pageJSON = pageUpdates } - var mergedModuleUpdates = (loadObject?.modulesJSON ?? [:]).mergingLeft(moduleUpdates ?? [:]) + let mergedModuleUpdates = (loadObject?.modulesJSON ?? [:]).mergingLeft(moduleUpdates ?? [:]) self.loadObject?.modulesJSON = mergedModuleUpdates + self.debugLog("Applying async update page model \(pageModel.debugDescription) and modules \(mergedModuleUpdates.keys) to page.") self.handleNewData(pageModel) } } @@ -115,6 +108,28 @@ import Combine self.observingForResponses = nil } + func pullUpdates(from notification: Notification) -> (pageUpdates: [String : Any]?, pageModel: PageModelProtocol?, moduleUpdates: [String : Any]?) { + // Get the page data. + let pageUpdates = extractInterestedPageType(from: notification.userInfo?.optionalDictionaryForKey(KeyPageMap) ?? [:]) + // Convert the page data into a new model. + var pageModel: PageModelProtocol? = nil + if let pageUpdates { + do { + // TODO: Rewiring to parse from plain JSON rather than this protocol indirection. + pageModel = try (self as? any TemplateProtocol & PageBehaviorHandlerProtocol & MVMCoreViewControllerProtocol)?.parseTemplate(pageJSON: pageUpdates) + } catch { + if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: self.pageType))") { + MVMCoreLoggingHandler.shared()?.addError(toLog: coreError) + } + } + } + // Get the module data. + let moduleUpdates = extractInterestedModules(from: notification.userInfo?.optionalDictionaryForKey(KeyModuleMap) ?? [:]) + debugLog("Receiving page \(pageModel?.pageType ?? "none") & \(moduleUpdates?.keys.description ?? "none") modules from \((notification.userInfo?["MVMCoreLoadObject"] as? MVMCoreLoadObject)?.requestParameters?.url?.absoluteString ?? "")") + // Bundle the transformations. + return (pageUpdates, pageModel, moduleUpdates) + } + open func pagesToListenFor() -> [String]? { guard let pageType = loadObject?.pageType else { return nil } return [pageType]