Digital PCT265 story ONEAPP-7249 - Reduce full cell reloads on updates.

This commit is contained in:
Hedden, Kyle Matthew 2024-05-02 19:21:29 -04:00
parent 3f77a261bc
commit fd296b9623
3 changed files with 29 additions and 71 deletions

View File

@ -1,63 +0,0 @@
//
// ReadableDecodingErrors.swift
// MVMCore
//
// Created by Kyle Hedden on 10/5/23.
// Copyright © 2023 myverizon. All rights reserved.
//
import Foundation
protocol HumanReadableDecodingErrorProtocol {
var readableDescription: String { get }
}
extension JSONError: HumanReadableDecodingErrorProtocol {
var readableDescription: String {
switch (self) {
case .other(let other):
if let other = other as? HumanReadableDecodingErrorProtocol {
return other.readableDescription
}
return description
default:
return description
}
}
}
extension ModelRegistry.Error: HumanReadableDecodingErrorProtocol {
var readableDescription: String {
switch (self) {
case .decoderErrorModelNotMapped(let identifier, let codingKey, let codingPath) where identifier != nil && codingKey != nil && codingPath != nil:
return "Model identifier \"\(identifier!)\" is not mapped for \"\(codingKey!.stringValue)\" @ \(codingPath!.map { return $0.stringValue })"
case .decoderErrorObjectNotPresent(let codingKey, let codingPath):
return "Required model \"\(codingKey.stringValue)\" was not found @ \(codingPath.map { return $0.stringValue })"
default:
return "Registry error: \((self as NSError).localizedFailureReason ?? self.localizedDescription)"
}
}
}
extension DecodingError: HumanReadableDecodingErrorProtocol {
var readableDescription: String {
switch (self) {
case .keyNotFound(let codingKey, let context):
return "Required key \(codingKey.stringValue) was not found @ \(context.codingPath.map { return $0.stringValue })"
case .valueNotFound(_, let context):
return "Value not found @ \(context.codingPath.map { return $0.stringValue })"
case .typeMismatch(_, let context):
return "Value type mismatch @ \(context.codingPath.map { return $0.stringValue })"
case .dataCorrupted(let context):
return "Data corrupted @ \(context.codingPath.map { return $0.stringValue })"
@unknown default:
return (self as NSError).localizedFailureReason ?? self.localizedDescription
}
}
}

View File

@ -97,12 +97,16 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false
super.updateUI(for: molecules)
molecules?.forEach({ molecule in
guard let molecules else { return }
// For updating individual specfied molecules. (Not a full table reload done in the base class.) These molecule types should remain the same type by replacement standards.
molecules.forEach({ molecule in
// Replace any top level cell data if required.
if let index = moleculesInfo?.firstIndex(where: { $0.molecule.id == molecule.id }) {
moleculesInfo?[index].molecule = molecule
}
newData(for: molecule)
})
newData(for: molecules)
}
open override func viewDidAppear(_ animated: Bool) {
@ -233,18 +237,27 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
}
open func newData(for molecule: MoleculeModelProtocol) {
newData(for: [molecule])
}
/// Refreshes all relevant cells for a given set of molecule models.
open func newData(for molecules: [MoleculeModelProtocol]) {
//Check header and footer if replace happens then return.
if updateHeaderFooterView(topView, with: molecule) ||
updateHeaderFooterView(bottomView, with: molecule, isHeader: false) {
return
molecules.forEach {
if updateHeaderFooterView(topView, with: $0) ||
updateHeaderFooterView(bottomView, with: $0, isHeader: false) {
return
}
}
guard let moleculesInfo = moleculesInfo else { return }
let indicies = moleculesInfo.indices.filter({ index -> Bool in
return moleculesInfo[index].molecule.findFirstMolecule(by: {
$0.moleculeName == molecule.moleculeName && equal(moleculeA: molecule, moleculeB: $0)
return moleculesInfo[index].molecule.findFirstMolecule(by: { existingMolecule in
molecules.contains { newMolecule in
existingMolecule.moleculeName == newMolecule.moleculeName && equal(moleculeA: existingMolecule, moleculeB: newMolecule)
}
}) != nil
})
@ -253,7 +266,14 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
let indexPaths = indicies.map {
return IndexPath(row: $0, section: 0)
}
tableView.reloadRows(at: indexPaths, with: .automatic)
if #available(iOS 15.0, *) {
// All rows should have been layed out already on the first newDataBuildScreen reload with the getMoleculeInfoList call. Therefore, we can be safe to assume the top level cell configuration will not be modified and only the child content will be updated allowing us to levearage this more efficient method.
tableView.reconfigureRows(at: indexPaths)
} else {
// A full reload can cause a flicker / animation. Better to avoid with above reconfigure method.
tableView.reloadRows(at: indexPaths, with: .automatic)
}
if let selectedIndex = selectedIndex {
tableView.selectRow(at: selectedIndex, animated: false, scrollPosition: .none)
}

View File

@ -60,6 +60,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior {
}
}
if moleculeModels.count > 0 {
// TODO: Getting dropped into the page update queue. Can we get this replaced without an async dispatch to avoid an animation?
delegateObject?.moleculeDelegate?.replaceMoleculeData(moleculeModels, completionHandler: nil)
}
}