add type check error logic. expand to replacement to the rest of parent molecules. lift full tree replacement to the template.

This commit is contained in:
Hedden, Kyle Matthew 2023-09-13 20:30:00 -04:00
parent 1f895ebdbd
commit caf995c0b0
45 changed files with 293 additions and 79 deletions

View File

@ -168,6 +168,7 @@
526A265E240D200500B0D828 /* ListTwoColumnCompareChanges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526A265D240D200500B0D828 /* ListTwoColumnCompareChanges.swift */; };
52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B201D024081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift */; };
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B201D124081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift */; };
5823ADF62AB0F7BA006045A7 /* ReplacementMoleculeBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5823ADF52AB0F7BA006045A7 /* ReplacementMoleculeBehavior.swift */; };
8D070BB0241B56530099AC56 /* ListRightVariableTotalDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D070BAF241B56530099AC56 /* ListRightVariableTotalDataModel.swift */; };
8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D070BB1241B56AD0099AC56 /* ListRightVariableTotalData.swift */; };
8D084AD02410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D084ACF2410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift */; };
@ -754,6 +755,7 @@
526A265D240D200500B0D828 /* ListTwoColumnCompareChanges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnCompareChanges.swift; sourceTree = "<group>"; };
52B201D024081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonAndPaymentMethod.swift; sourceTree = "<group>"; };
52B201D124081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonAndPaymentMethodModel.swift; sourceTree = "<group>"; };
5823ADF52AB0F7BA006045A7 /* ReplacementMoleculeBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplacementMoleculeBehavior.swift; sourceTree = "<group>"; };
8D070BAF241B56530099AC56 /* ListRightVariableTotalDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableTotalDataModel.swift; sourceTree = "<group>"; };
8D070BB1241B56AD0099AC56 /* ListRightVariableTotalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableTotalData.swift; sourceTree = "<group>"; };
8D084ACF2410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextBodyTextModel.swift; sourceTree = "<group>"; };
@ -1390,6 +1392,7 @@
D23A900826125FFB007E14CE /* GetContactBehavior.swift */,
D270E5662642F77300CDBED2 /* AddRemoveMoleculeBehavior.swift */,
22B678F829E7944E00CF4196 /* GetNotificationAuthStatusBehavior.swift */,
5823ADF52AB0F7BA006045A7 /* ReplacementMoleculeBehavior.swift */,
);
path = Behaviors;
sourceTree = "<group>";
@ -2752,6 +2755,7 @@
525239C02407BCFF00454969 /* ListTwoColumnPriceDetailsModel.swift in Sources */,
D2E2A99A23D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift in Sources */,
D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */,
5823ADF62AB0F7BA006045A7 /* ReplacementMoleculeBehavior.swift in Sources */,
D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */,
AA37CBD3251907200027344C /* StarsModel.swift in Sources */,
8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */,

View File

@ -20,6 +20,11 @@ public class HeadersH1ButtonModel: HeaderModel, MoleculeModelProtocol, ParentMol
[headlineBody, buttons]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|| replaceChildMolecule(at: &buttons, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -24,6 +24,15 @@ public class HeadersH1LandingPageHeaderModel: HeaderModel, MoleculeModelProtocol
[headline, headline2, subHeadline, body, link, buttons]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headline, with: molecule)
|| replaceChildMolecule(at: &headline2, with: molecule)
|| replaceChildMolecule(at: &subHeadline, with: molecule)
|| replaceChildMolecule(at: &body, with: molecule)
|| replaceChildMolecule(at: &link, with: molecule)
|| replaceChildMolecule(at: &buttons, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -20,6 +20,10 @@ public class HeadersH1NoButtonsBodyTextModel: HeaderModel, MoleculeModelProtocol
[headlineBody]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headlineBody, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -22,6 +22,11 @@ public class HeadersH2ButtonsModel: HeaderModel, MoleculeModelProtocol, ParentMo
[headlineBody, buttons]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|| replaceChildMolecule(at: &buttons, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -19,6 +19,11 @@ public class HeadersH2CaretLinkModel: HeaderModel, MoleculeModelProtocol, Parent
[headlineBody, caretLink]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|| replaceChildMolecule(at: &caretLink, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -21,6 +21,11 @@ public class HeadersH2LinkModel: HeaderModel, MoleculeModelProtocol, ParentMolec
[headlineBody, link]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|| replaceChildMolecule(at: &link, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -21,6 +21,10 @@ public class HeadersH2NoButtonsBodyTextModel: HeaderModel, MoleculeModelProtocol
[headlineBody]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headlineBody, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -25,6 +25,17 @@ public class HeadersH2PricingTwoRowsModel: HeaderModel, MoleculeModelProtocol, P
[headline, body, subBody, body2, subBody2, body3, subBody3].compactMap({$0})
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headline, with: molecule)
|| replaceChildMolecule(at: &body, with: molecule)
|| replaceChildMolecule(at: &subBody, with: molecule)
|| replaceChildMolecule(at: &body2, with: molecule)
|| replaceChildMolecule(at: &body2, with: molecule)
|| replaceChildMolecule(at: &subBody2, with: molecule)
|| replaceChildMolecule(at: &body3, with: molecule)
|| replaceChildMolecule(at: &subBody3, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -22,6 +22,11 @@ public class HeadersH2TinyButtonModel: HeaderModel, MoleculeModelProtocol, Paren
[headlineBody, button]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|| replaceChildMolecule(at: &button, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -20,6 +20,11 @@ open class ListLeftVariableCheckboxBodyTextModel: ListItemModel, MoleculeModelPr
[checkbox, headlineBody]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &checkbox, with: molecule)
|| replaceChildMolecule(at: &headlineBody, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -20,6 +20,11 @@ public class ListLeftVariableIconAllTextLinksModel: ListItemModel, MoleculeModel
return [image, eyebrowHeadlineBodyLink]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &image, with: molecule)
|| replaceChildMolecule(at: &eyebrowHeadlineBodyLink, with: molecule)
}
//--------------------------------------------------
// MARK: - Method
//--------------------------------------------------

View File

@ -21,6 +21,12 @@ public class ListLeftVariableIconWithRightCaretAllTextLinksModel: ListItemModel,
return [image, eyebrowHeadlineBodyLink, rightLabel]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &image, with: molecule)
|| replaceChildMolecule(at: &eyebrowHeadlineBodyLink, with: molecule)
|| replaceChildMolecule(at: &rightLabel, with: molecule)
}
//-----------------------------------------------------
// MARK: - Methods
//-----------------------------------------------------

View File

@ -21,6 +21,12 @@ public class ListLeftVariableIconWithRightCaretBodyTextModel: ListItemModel, Par
[image, headlineBody, rightLabel]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &image, with: molecule)
|| replaceChildMolecule(at: &headlineBody, with: molecule)
|| replaceChildMolecule(at: &rightLabel, with: molecule)
}
//-----------------------------------------------------
// MARK: - Methods
//-----------------------------------------------------

View File

@ -21,6 +21,12 @@ public class ListLeftVariableIconWithRightCaretModel: ListItemModel, ParentMolec
return [image, leftLabel, rightLabel]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &image, with: molecule)
|| replaceChildMolecule(at: &leftLabel, with: molecule)
|| replaceChildMolecule(at: &rightLabel, with: molecule)
}
//-----------------------------------------------------
// MARK: - Methods
//-----------------------------------------------------

View File

@ -20,9 +20,9 @@ open class ListLeftVariableRadioButtonBodyTextModel: ListItemModel, ParentMolecu
[radioButton, headlineBody]
}
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) -> Bool {
return replace(childMolecule: &radioButton, with: replacementMolecule)
|| replace(childMolecule: &headlineBody, with: replacementMolecule)
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &radioButton, with: replacementMolecule)
|| replaceChildMolecule(at: &headlineBody, with: replacementMolecule)
}
//-----------------------------------------------------

View File

@ -39,6 +39,10 @@ public class ListOneColumnFullWidthTextBodyTextModel: ListItemModel, MoleculeMod
return [headlineBody]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headlineBody, with: molecule)
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------

View File

@ -40,6 +40,11 @@ public class ListRightVariableButtonAllTextAndLinksModel: ListItemModel, Molecul
return [button, eyebrowHeadlineBodyLink]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &button, with: molecule)
|| replaceChildMolecule(at: &eyebrowHeadlineBodyLink, with: molecule)
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------

View File

@ -20,6 +20,11 @@ public class ListRightVariableRightCaretAllTextAndLinksModel: ListItemModel, Par
[rightLabel, eyebrowHeadlineBodyLink]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &rightLabel, with: molecule)
|| replaceChildMolecule(at: &eyebrowHeadlineBodyLink, with: molecule)
}
//-----------------------------------------------------
// MARK: - Methods
//-----------------------------------------------------

View File

@ -64,6 +64,12 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco
[eyebrow, title, subTitle].compactMap { (molecule: MoleculeModelProtocol?) in molecule }
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &eyebrow, with: molecule)
|| replaceChildMolecule(at: &title, with: molecule)
|| replaceChildMolecule(at: &subTitle, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -22,6 +22,11 @@ public class ListOneColumnFullWidthTextDividerSubsectionModel: ListItemModel, Mo
[headline, body].compactMap({$0})
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headline, with: molecule)
|| replaceChildMolecule(at: &body, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -22,6 +22,11 @@ public class ListOneColumnTextWithWhitespaceDividerShortModel: ListItemModel, Mo
[headline, body].compactMap({$0})
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headline, with: molecule)
|| replaceChildMolecule(at: &body, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -22,6 +22,11 @@ public class ListOneColumnTextWithWhitespaceDividerTallModel: ListItemModel, Mol
[headline, body].compactMap({$0})
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &headline, with: molecule)
|| replaceChildMolecule(at: &body, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -24,6 +24,11 @@ public class TwoButtonViewModel: ParentMoleculeModelProtocol {
return [primaryButton, secondaryButton].compactMap { $0 }
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &primaryButton, with: molecule)
|| replaceChildMolecule(at: &secondaryButton, with: molecule)
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------

View File

@ -18,10 +18,19 @@ public class CornerLabelsModel: ParentMoleculeModelProtocol {
public var bottomLeftLabel: LabelModel?
public var bottomRightLabel: LabelModel?
public var molecule: MoleculeModelProtocol?
public var children: [MoleculeModelProtocol] {
[molecule, topLeftLabel, topRightLabel, bottomLeftLabel, bottomRightLabel].compactMap { $0 }
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &self.molecule, with: molecule)
|| replaceChildMolecule(at: &topLeftLabel, with: molecule)
|| replaceChildMolecule(at: &topRightLabel, with: molecule)
|| replaceChildMolecule(at: &bottomLeftLabel, with: molecule)
|| replaceChildMolecule(at: &bottomRightLabel, with: molecule)
}
public init(with molecule: MoleculeModelProtocol?) {
self.molecule = molecule
}

View File

@ -20,6 +20,10 @@ open class MoleculeContainerModel: ContainerModel, MoleculeContainerModelProtoco
return [molecule]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &self.molecule, with: molecule)
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------

View File

@ -16,5 +16,9 @@ public extension MoleculeContainerModelProtocol {
var children: [MoleculeModelProtocol] {
return [molecule]
}
func replaceMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &molecule, with: replacementMolecule)
}
}

View File

@ -25,6 +25,13 @@ public class EyebrowHeadlineBodyLinkModel: MoleculeModelProtocol, ParentMolecule
[eyebrow, headline, body, link].compactMap { (molecule: MoleculeModelProtocol?) in molecule }
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &eyebrow, with: molecule)
|| replaceChildMolecule(at: &headline, with: molecule)
|| replaceChildMolecule(at: &body, with: molecule)
|| replaceChildMolecule(at: &link, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -24,9 +24,9 @@
[headline, body].compactMap { $0 }
}
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) -> Bool {
return replace(childMolecule:&headline, with: replacementMolecule)
|| replace(childMolecule:&body, with: replacementMolecule)
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at:&headline, with: replacementMolecule)
|| replaceChildMolecule(at:&body, with: replacementMolecule)
}
//--------------------------------------------------

View File

@ -180,4 +180,8 @@ extension CarouselModel {
return molecules
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(in: &molecules, with: molecule)
}
}

View File

@ -21,22 +21,6 @@
public var spacing: CGFloat = Padding.Four
public var useStackSpacingBeforeFirstItem = false
public var children: [MoleculeModelProtocol] {
return molecules
}
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) -> Bool {
// IDEALLY:
//return replace(inChildMolecules: &molecules, with: replacementMolecule)
guard let replacementMolecule = replacementMolecule as? StackItemModelProtocol & MoleculeModelProtocol else { return false }
guard let matchingIndex = molecules.firstIndex(where: { molecule in
molecule.id == replacementMolecule.id
}) else { return false }
molecules[matchingIndex] = replacementMolecule
return true
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -19,4 +19,8 @@ extension StackModelProtocol {
public var children: [MoleculeModelProtocol] { return molecules }
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(in: &molecules, with: molecule)
}
}

View File

@ -8,11 +8,44 @@
import Foundation
public protocol ParentMoleculeModelProtocol: MoleculeModelProtocol, AnyObject {
public protocol ParentModelProtocol: MoleculeTreeTraversalProtocol, AnyObject {
var children: [MoleculeModelProtocol] { get }
func replaceChildMolecule(with molecule: MoleculeModelProtocol) -> Bool
func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool
}
public extension ParentModelProtocol {
/// Top level test to replace child molecules. Each parent molecule should attempt to replace.
func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool { return false }
/// Helper function for replacing molecules.
func replaceChildMolecule<T>(at childMolecule: inout T, with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
guard let childIdMolecule = childMolecule as? MoleculeModelProtocol else { return false }
if childIdMolecule.id == replacementMolecule.id {
guard let replacementMolecule = replacementMolecule as? T else {
throw MolecularError.error("Molecular replacement '\(replacementMolecule.id)' does not type match \(type(of: T.self)) of \(type(of: self))")
}
childMolecule = replacementMolecule
return true
}
return false
}
func replaceChildMolecule<T>(in molecules: inout [T], with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
guard let moleculeIdModels = molecules as? [MoleculeModelProtocol], let matchingIndex = moleculeIdModels.firstIndex(where: {
$0.id == replacementMolecule.id
}) else { return false }
guard let replacementMolecule = replacementMolecule as? T else {
throw MolecularError.error("Molecular replacement '\(replacementMolecule.id)' does not type match \(type(of: T.self)) of \(type(of: self))")
}
molecules[matchingIndex] = replacementMolecule
return true
}
}
public protocol ParentMoleculeModelProtocol: ParentModelProtocol, MoleculeModelProtocol {
}
public extension ParentMoleculeModelProtocol {
@ -56,33 +89,4 @@ public extension ParentMoleculeModelProtocol {
}
// if options == .leafOnly don't call on self.
}
/// Top level test to replace child molecules. Each parent molecule should attempt to replace.
func replaceChildMolecule(with molecule: MoleculeModelProtocol) -> Bool { return false }
/// Helper function for replacing molecules.
func replace<T: MoleculeModelProtocol>(childMolecule: inout T?, with replacementMolecule: MoleculeModelProtocol) -> Bool {
if childMolecule != nil, childMolecule?.id == replacementMolecule.id, let newHeadline = replacementMolecule as? T {
childMolecule = newHeadline
return true
}
return false
}
func replace<T: MoleculeModelProtocol>(childMolecule: inout T, with replacementMolecule: MoleculeModelProtocol) -> Bool {
if childMolecule.id == replacementMolecule.id, let newHeadline = replacementMolecule as? T {
childMolecule = newHeadline
return true
}
return false
}
func replace<T>(inChildMolecules molecules: inout [T], with replacementMolecule: MoleculeModelProtocol) -> Bool where T: MoleculeModelProtocol {
guard let replacementMolecule = replacementMolecule as? T else { return false }
guard let matchingIndex = molecules.firstIndex(where: { molecule in
molecule.id == replacementMolecule.id
}) else { return false }
molecules[matchingIndex] = replacementMolecule
return true
}
}

View File

@ -7,13 +7,17 @@
//
public protocol TemplateModelProtocol: PageModelProtocol, ModelProtocol, MoleculeTreeTraversalProtocol {
public protocol TemplateModelProtocol: PageModelProtocol, ModelProtocol, MoleculeTreeTraversalProtocol, ParentModelProtocol {
var template: String { get }
var rootMolecules: [MoleculeModelProtocol] { get }
}
public extension TemplateModelProtocol {
var children: [MoleculeModelProtocol] {
return rootMolecules
}
var template: String {
get { return Self.identifier }
}
@ -33,4 +37,28 @@ public extension TemplateModelProtocol {
func depthFirstTraverse(options: TreeTraversalOptions, depth: Int, onVisit: (Int, MoleculeModelProtocol, inout Bool) -> Void) {
return rootMolecules.depthFirstTraverse(options: options, depth: depth, onVisit: onVisit)
}
func replaceMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
// Attempt root level replacement on the template model first.
if try self.replaceChildMolecule(with: replacementMolecule) {
return true
}
var didReplaceMolecule = false
var possibleError: Error?
// Dive into each root thereafter.
depthFirstTraverse(options: .parentFirst, depth: 0) { depth, molecule, stop in
guard let parentMolecule = molecule as? ParentMoleculeModelProtocol else { return }
do {
didReplaceMolecule = try parentMolecule.replaceChildMolecule(with: replacementMolecule)
} catch {
possibleError = error
}
stop = didReplaceMolecule || possibleError != nil
}
if let error = possibleError {
throw error
}
return didReplaceMolecule
}
}

View File

@ -9,6 +9,8 @@
public protocol MoleculeDelegateProtocol: AnyObject {
func getTemplateModel() -> TemplateModelProtocol?
func getRootMolecules() -> [MoleculeModelProtocol]
/// returns a module for the corresponding module name.
@ -22,6 +24,10 @@ public protocol MoleculeDelegateProtocol: AnyObject {
extension MoleculeDelegateProtocol {
public func getRootMolecules() -> [MoleculeModelProtocol] {
getTemplateModel()?.rootMolecules ?? []
}
public func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { }
public func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? {
@ -42,6 +48,11 @@ extension MoleculeDelegateProtocol {
}
extension MoleculeDelegateProtocol where Self: TemplateProtocol {
public func getTemplateModel() -> TemplateModelProtocol? {
return templateModel
}
public func getRootMolecules() -> [MoleculeModelProtocol] {
templateModel?.rootMolecules ?? []
}

View File

@ -38,7 +38,7 @@ public extension MoleculeTreeTraversalProtocol {
func printMolecules(options: TreeTraversalOptions = .parentFirst) {
depthFirstTraverse(options: options, depth: 1) { depth, molecule, stop in
print("\(String(repeating: ">>", count: depth)) \"\(molecule.moleculeName)\" [\(molecule)]")
print("\(String(repeating: ">>", count: depth)) \"\(molecule.moleculeName)\" [\(molecule): \(molecule.id)]")
}
}
@ -50,14 +50,4 @@ public extension MoleculeTreeTraversalProtocol {
return accumulator
}
}
func replaceMolecule(with replacementMolecule: MoleculeModelProtocol) -> Bool {
var didReplaceMolecule = false
depthFirstTraverse(options: .parentFirst, depth: 0) { depth, molecule, stop in
guard let parentMolecule = molecule as? ParentMoleculeModelProtocol else { return }
didReplaceMolecule = parentMolecule.replaceChildMolecule(with: replacementMolecule)
stop = didReplaceMolecule
}
return didReplaceMolecule
}
}

View File

@ -32,7 +32,11 @@ import Foundation
public var tabBarIndex: Int?
public var shouldMaskScreenWhileRecording: Bool?
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &navigationBar, with: molecule)
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------

View File

@ -22,6 +22,11 @@
}
return super.rootMolecules
}
public override func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try super.replaceChildMolecule(with: molecule)
|| (molecules != nil && replaceChildMolecule(in: &(molecules!), with: molecule))
}
//--------------------------------------------------
// MARK: - Initializer

View File

@ -26,6 +26,12 @@
return super.rootMolecules
}
public override func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try super.replaceChildMolecule(with: molecule)
|| (molecules != nil && replaceChildMolecule(in: &(molecules!), with: molecule))
|| replaceChildMolecule(at: &line, with: molecule)
}
/// This template requires content.
func validateModelHasContent() throws {
if header == nil,

View File

@ -16,7 +16,13 @@
public var moleculeStack: StackModel
public override var rootMolecules: [MoleculeModelProtocol] {
[navigationBar, header, moleculeStack, footer].compactMap { $0 }
super.rootMolecules + [moleculeStack]
}
public override func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try super.replaceChildMolecule(with: molecule)
|| replaceChildMolecule(at: &navigationBar, with: molecule)
|| replaceChildMolecule(at: &moleculeStack, with: molecule)
}
//--------------------------------------------------

View File

@ -21,6 +21,12 @@
[navigationBar, header, footer].compactMap { $0 }
}
public override func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try super.replaceChildMolecule(with: molecule)
|| replaceChildMolecule(at: &header, with: molecule)
|| replaceChildMolecule(at: &footer, with: molecule)
}
//--------------------------------------------------
// MARK: - Init
//--------------------------------------------------

View File

@ -22,6 +22,11 @@
return super.rootMolecules
}
public override func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try super.replaceChildMolecule(with: molecule)
|| replaceChildMolecule(at: &middle, with: molecule)
}
//--------------------------------------------------
// MARK: - Init
//--------------------------------------------------

View File

@ -513,9 +513,9 @@ import MVMCore
// MARK: - MoleculeDelegateProtocol
//--------------------------------------------------
open func getRootMolecules() -> [MoleculeModelProtocol] {
model?.rootMolecules ?? []
}
open func getTemplateModel() -> TemplateModelProtocol? { model }
open func getRootMolecules() -> [MoleculeModelProtocol] { model?.rootMolecules ?? [] }
// Needed otherwise when subclassed, the extension gets called.
open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { }

View File

@ -9,27 +9,35 @@
import Foundation
import MVMCore
public class ReplacableMoleculeBehaviorModel: PageBehaviorModelProtocol {
public class ReplaceableMoleculeBehaviorModel: PageBehaviorModelProtocol {
public class var identifier: String { "replaceMoleculeBehavior" }
public var shouldAllowMultipleInstances: Bool { true }
public var moleculeIds: [String]
}
public class ReplacableMoleculeBehavior: PageMoleculeTransformationBehavior {
public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior {
var moleculeIds: [String]
public required init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) {
moleculeIds = (model as! ReplacableMoleculeBehaviorModel).moleculeIds
moleculeIds = (model as! ReplaceableMoleculeBehaviorModel).moleculeIds
}
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {
var shouldRefreshUI = false
guard let templateModel = delegateObject?.moleculeDelegate?.getTemplateModel() else { return }
templateModel.printMolecules()
for moleculeId in moleculeIds {
guard let replacementModel = delegateObject?.moleculeDelegate?.getModuleWithName(moleculeId) else { continue }
let didReplace = rootMolecules.contains(where: { model in
return model.replaceMolecule(with: replacementModel)
})
shouldRefreshUI = shouldRefreshUI || didReplace
do {
let didReplace = try templateModel.replaceMolecule(with: replacementModel)
if !didReplace {
MVMCoreLoggingHandler.addError(toLog: MVMCoreErrorObject(title: nil, messageToLog: "Failed to find '\(moleculeId)' in the current screen.", code: ErrorCode.viewControllerProcessingJSON.rawValue, domain: ErrorDomainSystem, location: String(describing: type(of: self)))!)
}
} catch {
MVMCoreLoggingHandler.addError(toLog: MVMCoreErrorObject.createErrorObject(for: error, location: String(describing: type(of: self)))!)
}
}
}

View File

@ -227,7 +227,7 @@ open class CoreUIModelMapping: ModelMapping {
ModelRegistry.register(handler: PageGetContactBehavior.self, for: PageGetContactBehaviorModel.self)
ModelRegistry.register(handler: AddRemoveMoleculesBehavior.self, for: AddRemoveMoleculesBehaviorModel.self)
ModelRegistry.register(handler: GetNotificationAuthStatusBehavior.self, for: GetNotificationAuthStatusBehaviorModel.self)
ModelRegistry.register(handler: ReplacableMoleculeBehavior.self, for: ReplacableMoleculeBehaviorModel.self)
ModelRegistry.register(handler: ReplaceableMoleculeBehavior.self, for: ReplaceableMoleculeBehaviorModel.self)
}
open override class func registerActions() {