Digital PCT265 story PCT-135: Rewire to allow the page transformation behavior to list all changes performed to the model tree.

This commit is contained in:
Hedden, Kyle Matthew 2024-05-20 21:36:26 -04:00
parent f74bea64c2
commit d377ec84b7
6 changed files with 1275 additions and 22 deletions

View File

@ -175,6 +175,7 @@
583335632BF6509C001D90D7 /* UAD_page_model.json in Resources */ = {isa = PBXBuildFile; fileRef = 583335622BF6509C001D90D7 /* UAD_page_model.json */; };
583335652BF6A5C3001D90D7 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583335642BF6A5C3001D90D7 /* TestUtils.swift */; };
583335672BF6DCD0001D90D7 /* UAD_page_model_2.json in Resources */ = {isa = PBXBuildFile; fileRef = 583335662BF6DCD0001D90D7 /* UAD_page_model_2.json */; };
5833356D2BFBF51C001D90D7 /* UAD_page_model_3.json in Resources */ = {isa = PBXBuildFile; fileRef = 5833356C2BFBF51C001D90D7 /* UAD_page_model_3.json */; };
5846ABF62B4762A600FA6C76 /* PollingBehaviorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5846ABF52B4762A600FA6C76 /* PollingBehaviorModel.swift */; };
58A9DD7D2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A9DD7C2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift */; };
58E7561D2BE04C320088BB5D /* MoleculeComparisonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E7561C2BE04C320088BB5D /* MoleculeComparisonProtocol.swift */; };
@ -797,6 +798,7 @@
583335622BF6509C001D90D7 /* UAD_page_model.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = UAD_page_model.json; sourceTree = "<group>"; };
583335642BF6A5C3001D90D7 /* TestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = "<group>"; };
583335662BF6DCD0001D90D7 /* UAD_page_model_2.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = UAD_page_model_2.json; sourceTree = "<group>"; };
5833356C2BFBF51C001D90D7 /* UAD_page_model_3.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = UAD_page_model_3.json; sourceTree = "<group>"; };
5846ABF52B4762A600FA6C76 /* PollingBehaviorModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PollingBehaviorModel.swift; sourceTree = "<group>"; };
5878F0A42BD7E68800ADE23D /* mvmcoreui.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = mvmcoreui.xcconfig; sourceTree = "<group>"; };
5878F0A52BD7E6BE00ADE23D /* mvmcoreui_dev.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = mvmcoreui_dev.xcconfig; sourceTree = "<group>"; };
@ -1558,6 +1560,7 @@
children = (
583335662BF6DCD0001D90D7 /* UAD_page_model_2.json */,
583335622BF6509C001D90D7 /* UAD_page_model.json */,
5833356C2BFBF51C001D90D7 /* UAD_page_model_3.json */,
);
path = Modelling;
sourceTree = "<group>";
@ -2740,6 +2743,7 @@
buildActionMask = 2147483647;
files = (
583335672BF6DCD0001D90D7 /* UAD_page_model_2.json in Resources */,
5833356D2BFBF51C001D90D7 /* UAD_page_model_3.json in Resources */,
583335632BF6509C001D90D7 /* UAD_page_model.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -256,18 +256,14 @@ import MVMCore
var behaviorUpdatedModels = [MoleculeModelProtocol]()
if var newTemplateModel = newPageModel as? TemplateModelProtocol {
executeBehaviors { (behavior: PageMoleculeTransformationBehavior) in
if let updatedMolecules = behavior.onPageNew(rootMolecules: newTemplateModel.rootMolecules, delegateObjectIVar) {
var changes = [any MoleculeModelProtocol]()
if let updatedMolecules = behavior.onPageNew(rootMolecules: newTemplateModel.rootMolecules, delegateObjectIVar, changes: &changes) {
updatedMolecules.forEach { molecule in
// Replace again in case there is a template level child.
if let replaced = try? newTemplateModel.replaceChildMolecule(with: molecule) {
// Only recognize the molecules that actually changed.
if let replaced = replaced as? ParentMoleculeModelProtocol, let molecule = molecule as? ParentMoleculeModelProtocol {
let diffs: [MoleculeModelProtocol] = replaced.findAllTheirsNotEqual(against: molecule)
debugLog("Behavior updated \(diffs) in template model.")
behaviorUpdatedModels.append(contentsOf: diffs)
} else if !replaced.isEqual(to: molecule) {
debugLog("Behavior updated \(molecule) in template model.")
behaviorUpdatedModels.append(molecule) // Need to specifically trace molecule updates here as replacements are modifying the original tree. (We don't have a deep copy.)
}
debugLog("Behavior updated \(changes) in template model.")
behaviorUpdatedModels.append(contentsOf: changes)
} else {
debugLog("Failed to replace \(molecule) in the template model.")
}

View File

@ -31,6 +31,7 @@ public extension PageBehaviorProtocol {
public protocol PageMoleculeTransformationBehavior: PageBehaviorProtocol {
func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) -> [MoleculeModelProtocol]?
func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?, changes: inout [MoleculeModelProtocol]) -> [MoleculeModelProtocol]?
func willSetupMolecule(with model: MoleculeModelProtocol, updating view: MoleculeViewProtocol?)
func didSetupMolecule(view: MoleculeViewProtocol, withModel: MoleculeModelProtocol)
func willSetupNavigationBar(with model: NavigationItemModelProtocol, updating view: UINavigationBar)
@ -41,7 +42,11 @@ public protocol PageMoleculeTransformationBehavior: PageBehaviorProtocol {
public extension PageMoleculeTransformationBehavior {
// All optional.
func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) -> [MoleculeModelProtocol]? { return nil }
func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) -> [MoleculeModelProtocol]? {
var changes = [any MoleculeModelProtocol]()
return onPageNew(rootMolecules: rootMolecules, delegateObject, changes: &changes)
}
func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?, changes: inout [MoleculeModelProtocol]) -> [MoleculeModelProtocol]? { return nil }
func willSetupMolecule(with model: MoleculeModelProtocol, updating view: MoleculeViewProtocol?) {}
func didSetupMolecule(view: MoleculeViewProtocol, withModel: MoleculeModelProtocol) {}
func willSetupNavigationBar(with model: NavigationItemModelProtocol, updating view: UINavigationBar) {}

View File

@ -44,9 +44,8 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
Self.debugLog("Initializing for \((model as! ReplaceableMoleculeBehaviorModel).moleculeIds)")
}
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) -> [MoleculeModelProtocol]? {
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?, changes: inout [MoleculeModelProtocol]) -> [MoleculeModelProtocol]? {
self.delegateObject = delegateObject
modulesToListenFor = moleculeIds
let moleculeModels = moleculeIds.compactMap { moleculeId in
do {
@ -61,23 +60,23 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
}
}
return findAndReplace(moleculeModels, in: rootMolecules)
return findAndReplace(moleculeModels, in: rootMolecules, changes: &changes)
}
fileprivate func findAndReplace(_ moleculeModels: [any MoleculeModelProtocol], in rootMolecules: [any MoleculeModelProtocol]) -> [any MoleculeModelProtocol]? {
fileprivate func findAndReplace(_ moleculeModels: [any MoleculeModelProtocol], in rootMolecules: [any MoleculeModelProtocol], changes: inout [MoleculeModelProtocol]) -> [any MoleculeModelProtocol]? {
debugLog("attempting to replace \(moleculeModels.map { $0.id }) in \(rootMolecules)")
var hasReplacement = false
var changeList = [any MoleculeModelProtocol]()
let updatedRootMolecules = rootMolecules.map { rootMolecule in
// Top level check to return a new root molecule.
if let updatedMolecule = moleculeModels.first(where: { rootMolecule.id == $0.id }) {
guard !updatedMolecule.isEqual(to: rootMolecule) else {
debugLog("molecule \(updatedMolecule) is the same as \(rootMolecule). skipping...")
debugLog("top molecule \(updatedMolecule) is the same as \(rootMolecule). skipping...")
return rootMolecule
}
debugLog("replacing \(rootMolecule) with \(updatedMolecule)")
debugLog("top replacing \(rootMolecule) with \(updatedMolecule)")
logUpdated(molecule: updatedMolecule)
hasReplacement = true
changeList.append(updatedMolecule)
return updatedMolecule
}
@ -89,12 +88,12 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
if let replacedMolecule = try parentMolecule.replaceChildMolecule(with: newMolecule) {
guard !replacedMolecule.isEqual(to: newMolecule) else {
// Note: Slight risk here of replacing the something in the original tree and misreporting that is it not replaced based on equality.
debugLog("molecule \(newMolecule) is the same as \(replacedMolecule). skipping...")
debugLog("deep molecule \(newMolecule) is the same as \(replacedMolecule). skipping...")
return
}
debugLog("replacing \(replacedMolecule) with \(newMolecule)")
debugLog("deep replacing \(replacedMolecule) with \(newMolecule)")
logUpdated(molecule: newMolecule)
hasReplacement = true
changeList.append(newMolecule)
}
} catch {
let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: String(describing: type(of: self)))!
@ -106,6 +105,8 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
}
return parentMolecule
}
let hasReplacement = !changeList.isEmpty
changes.append(contentsOf: changeList)
debugLog("replacing \(hasReplacement ? updatedRootMolecules.count : 0) molecules")
return hasReplacement ? updatedRootMolecules : nil
}

File diff suppressed because it is too large Load Diff

View File

@ -189,6 +189,7 @@ final class MVMCoreUITests: XCTestCase {
let listTemplateModel1 = try JSONDecoder().decode(ListPageTemplateModel.self, from: getFileData("UAD_page_model"))
let listTemplateModel2 = try JSONDecoder().decode(ListPageTemplateModel.self, from: getFileData("UAD_page_model"))
let listTemplateModel3 = try JSONDecoder().decode(ListPageTemplateModel.self, from: getFileData("UAD_page_model_2"))
let listTemplateModel4 = try JSONDecoder().decode(ListPageTemplateModel.self, from: getFileData("UAD_page_model_3"))
let results = listTemplateModel1.findFirst(in: listTemplateModel2, failing: { $0.isEqual(to: $1) })
XCTAssertFalse(results.matched)
@ -199,6 +200,9 @@ final class MVMCoreUITests: XCTestCase {
XCTAssertTrue(results2.matched)
XCTAssertFalse(listTemplateModel1.deepEquals(to: listTemplateModel3))
XCTAssertTrue(listTemplateModel1.isDeeplyVisuallyEquivalent(to: listTemplateModel3))
let results3: [MoleculeModelProtocol] = listTemplateModel1.findAllTheirsNotEqual(against: listTemplateModel4)
XCTAssertTrue(results3.count == 2)
}
func testPageEqualityPerformance() throws {