Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui.git into feature/vds_batch_three
# Conflicts: # MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
commit
4e8bbd2ea5
@ -168,6 +168,11 @@
|
|||||||
526A265E240D200500B0D828 /* ListTwoColumnCompareChanges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526A265D240D200500B0D828 /* ListTwoColumnCompareChanges.swift */; };
|
526A265E240D200500B0D828 /* ListTwoColumnCompareChanges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526A265D240D200500B0D828 /* ListTwoColumnCompareChanges.swift */; };
|
||||||
52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B201D024081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift */; };
|
52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B201D024081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift */; };
|
||||||
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B201D124081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift */; };
|
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B201D124081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift */; };
|
||||||
|
5822720B2B1FC55F00F75BAE /* AccessibilityHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582272092B1FC55F00F75BAE /* AccessibilityHandler.swift */; };
|
||||||
|
5822720C2B1FC55F00F75BAE /* RotorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5822720A2B1FC55F00F75BAE /* RotorHandler.swift */; };
|
||||||
|
5846ABF62B4762A600FA6C76 /* PollingBehaviorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5846ABF52B4762A600FA6C76 /* PollingBehaviorModel.swift */; };
|
||||||
|
5870636F2ACF238E00CA18D5 /* ReadableDecodingErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5870636E2ACF238E00CA18D5 /* ReadableDecodingErrors.swift */; };
|
||||||
|
58A9DD7D2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A9DD7C2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift */; };
|
||||||
608211282AC6B57E00C3FC39 /* MVMCoreUILoggingHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */; };
|
608211282AC6B57E00C3FC39 /* MVMCoreUILoggingHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */; };
|
||||||
7199C8162A4F3A64001568B7 /* AccessibilityHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7199C8152A4F3A64001568B7 /* AccessibilityHandler.swift */; };
|
7199C8162A4F3A64001568B7 /* AccessibilityHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7199C8152A4F3A64001568B7 /* AccessibilityHandler.swift */; };
|
||||||
71BE969E2AD96BE6000B5DB7 /* RotorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BE969D2AD96BE6000B5DB7 /* RotorHandler.swift */; };
|
71BE969E2AD96BE6000B5DB7 /* RotorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BE969D2AD96BE6000B5DB7 /* RotorHandler.swift */; };
|
||||||
@ -387,7 +392,7 @@
|
|||||||
D23A8FEE26122F7D007E14CE /* VisibleBehaviorForVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A8FED26122F7D007E14CE /* VisibleBehaviorForVideo.swift */; };
|
D23A8FEE26122F7D007E14CE /* VisibleBehaviorForVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A8FED26122F7D007E14CE /* VisibleBehaviorForVideo.swift */; };
|
||||||
D23A8FF82612308D007E14CE /* PageBehaviorProtocolRequirer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A8FF72612308D007E14CE /* PageBehaviorProtocolRequirer.swift */; };
|
D23A8FF82612308D007E14CE /* PageBehaviorProtocolRequirer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A8FF72612308D007E14CE /* PageBehaviorProtocolRequirer.swift */; };
|
||||||
D23A8FFB26123189007E14CE /* PageBehaviorModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A8FFA26123189007E14CE /* PageBehaviorModelProtocol.swift */; };
|
D23A8FFB26123189007E14CE /* PageBehaviorModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A8FFA26123189007E14CE /* PageBehaviorModelProtocol.swift */; };
|
||||||
D23A90002612347A007E14CE /* PageBehaviorHandlerModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A8FFF2612347A007E14CE /* PageBehaviorHandlerModelProtocol.swift */; };
|
D23A90002612347A007E14CE /* PageBehaviorContainerModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A8FFF2612347A007E14CE /* PageBehaviorContainerModelProtocol.swift */; };
|
||||||
D23A9004261234CE007E14CE /* PageBehaviorHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A9003261234CE007E14CE /* PageBehaviorHandlerProtocol.swift */; };
|
D23A9004261234CE007E14CE /* PageBehaviorHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A9003261234CE007E14CE /* PageBehaviorHandlerProtocol.swift */; };
|
||||||
D23A900926125FFB007E14CE /* GetContactBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A900826125FFB007E14CE /* GetContactBehavior.swift */; };
|
D23A900926125FFB007E14CE /* GetContactBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A900826125FFB007E14CE /* GetContactBehavior.swift */; };
|
||||||
D23A90682614B0B4007E14CE /* CoreUIModelMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A90672614B0B4007E14CE /* CoreUIModelMapping.swift */; };
|
D23A90682614B0B4007E14CE /* CoreUIModelMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23A90672614B0B4007E14CE /* CoreUIModelMapping.swift */; };
|
||||||
@ -766,6 +771,11 @@
|
|||||||
526A265D240D200500B0D828 /* ListTwoColumnCompareChanges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnCompareChanges.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
52B201D124081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableRadioButtonAndPaymentMethodModel.swift; sourceTree = "<group>"; };
|
||||||
|
582272092B1FC55F00F75BAE /* AccessibilityHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessibilityHandler.swift; sourceTree = "<group>"; };
|
||||||
|
5822720A2B1FC55F00F75BAE /* RotorHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RotorHandler.swift; sourceTree = "<group>"; };
|
||||||
|
5846ABF52B4762A600FA6C76 /* PollingBehaviorModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PollingBehaviorModel.swift; sourceTree = "<group>"; };
|
||||||
|
5870636E2ACF238E00CA18D5 /* ReadableDecodingErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadableDecodingErrors.swift; sourceTree = "<group>"; };
|
||||||
|
58A9DD7C2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaceableMoleculeBehaviorModel.swift; sourceTree = "<group>"; };
|
||||||
608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUILoggingHandler.swift; sourceTree = "<group>"; };
|
608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUILoggingHandler.swift; sourceTree = "<group>"; };
|
||||||
7199C8152A4F3A64001568B7 /* AccessibilityHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityHandler.swift; sourceTree = "<group>"; };
|
7199C8152A4F3A64001568B7 /* AccessibilityHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityHandler.swift; sourceTree = "<group>"; };
|
||||||
71BE969D2AD96BE6000B5DB7 /* RotorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotorHandler.swift; sourceTree = "<group>"; };
|
71BE969D2AD96BE6000B5DB7 /* RotorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotorHandler.swift; sourceTree = "<group>"; };
|
||||||
@ -985,7 +995,7 @@
|
|||||||
D23A8FED26122F7D007E14CE /* VisibleBehaviorForVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibleBehaviorForVideo.swift; sourceTree = "<group>"; };
|
D23A8FED26122F7D007E14CE /* VisibleBehaviorForVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibleBehaviorForVideo.swift; sourceTree = "<group>"; };
|
||||||
D23A8FF72612308D007E14CE /* PageBehaviorProtocolRequirer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageBehaviorProtocolRequirer.swift; sourceTree = "<group>"; };
|
D23A8FF72612308D007E14CE /* PageBehaviorProtocolRequirer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageBehaviorProtocolRequirer.swift; sourceTree = "<group>"; };
|
||||||
D23A8FFA26123189007E14CE /* PageBehaviorModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageBehaviorModelProtocol.swift; sourceTree = "<group>"; };
|
D23A8FFA26123189007E14CE /* PageBehaviorModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageBehaviorModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
D23A8FFF2612347A007E14CE /* PageBehaviorHandlerModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageBehaviorHandlerModelProtocol.swift; sourceTree = "<group>"; };
|
D23A8FFF2612347A007E14CE /* PageBehaviorContainerModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageBehaviorContainerModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
D23A9003261234CE007E14CE /* PageBehaviorHandlerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageBehaviorHandlerProtocol.swift; sourceTree = "<group>"; };
|
D23A9003261234CE007E14CE /* PageBehaviorHandlerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageBehaviorHandlerProtocol.swift; sourceTree = "<group>"; };
|
||||||
D23A900826125FFB007E14CE /* GetContactBehavior.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetContactBehavior.swift; sourceTree = "<group>"; };
|
D23A900826125FFB007E14CE /* GetContactBehavior.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetContactBehavior.swift; sourceTree = "<group>"; };
|
||||||
D23A90672614B0B4007E14CE /* CoreUIModelMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreUIModelMapping.swift; sourceTree = "<group>"; };
|
D23A90672614B0B4007E14CE /* CoreUIModelMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreUIModelMapping.swift; sourceTree = "<group>"; };
|
||||||
@ -1224,10 +1234,10 @@
|
|||||||
D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */,
|
D2E2A9A023E095AB000B42E6 /* ButtonModelProtocol.swift */,
|
||||||
014AA72323C501E2006F3E93 /* ContainerModelProtocol.swift */,
|
014AA72323C501E2006F3E93 /* ContainerModelProtocol.swift */,
|
||||||
D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */,
|
D23EA7FA2475F09800D60C34 /* CarouselItemProtocol.swift */,
|
||||||
|
01EB3683236097C0006832FA /* MoleculeModelProtocol.swift */,
|
||||||
012A88C3238D86E600FE3DA1 /* CarouselItemModelProtocol.swift */,
|
012A88C3238D86E600FE3DA1 /* CarouselItemModelProtocol.swift */,
|
||||||
012A88B0238C880100FE3DA1 /* CarouselPagingModelProtocol.swift */,
|
012A88B0238C880100FE3DA1 /* CarouselPagingModelProtocol.swift */,
|
||||||
EA05EFAA278DE53600828819 /* ClearableModelProtocol.swift */,
|
EA05EFAA278DE53600828819 /* ClearableModelProtocol.swift */,
|
||||||
01EB3683236097C0006832FA /* MoleculeModelProtocol.swift */,
|
|
||||||
012A889B23889E8400FE3DA1 /* TemplateModelProtocol.swift */,
|
012A889B23889E8400FE3DA1 /* TemplateModelProtocol.swift */,
|
||||||
D28A837823C7D5BC00DFE4FC /* PageModelProtocol.swift */,
|
D28A837823C7D5BC00DFE4FC /* PageModelProtocol.swift */,
|
||||||
011B58EF23A2AA980085F53C /* ListItemModelProtocol.swift */,
|
011B58EF23A2AA980085F53C /* ListItemModelProtocol.swift */,
|
||||||
@ -1343,7 +1353,7 @@
|
|||||||
children = (
|
children = (
|
||||||
D23A8FF72612308D007E14CE /* PageBehaviorProtocolRequirer.swift */,
|
D23A8FF72612308D007E14CE /* PageBehaviorProtocolRequirer.swift */,
|
||||||
D23A8FFA26123189007E14CE /* PageBehaviorModelProtocol.swift */,
|
D23A8FFA26123189007E14CE /* PageBehaviorModelProtocol.swift */,
|
||||||
D23A8FFF2612347A007E14CE /* PageBehaviorHandlerModelProtocol.swift */,
|
D23A8FFF2612347A007E14CE /* PageBehaviorContainerModelProtocol.swift */,
|
||||||
D23A9003261234CE007E14CE /* PageBehaviorHandlerProtocol.swift */,
|
D23A9003261234CE007E14CE /* PageBehaviorHandlerProtocol.swift */,
|
||||||
27F973522466074500CAB5C5 /* PageBehaviorProtocol.swift */,
|
27F973522466074500CAB5C5 /* PageBehaviorProtocol.swift */,
|
||||||
);
|
);
|
||||||
@ -1409,6 +1419,8 @@
|
|||||||
27F973512466071600CAB5C5 /* Behaviors */ = {
|
27F973512466071600CAB5C5 /* Behaviors */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
5846ABF52B4762A600FA6C76 /* PollingBehaviorModel.swift */,
|
||||||
|
58A9DD7C2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift */,
|
||||||
0A1C30972620F61A00B47F3B /* Protocols */,
|
0A1C30972620F61A00B47F3B /* Protocols */,
|
||||||
27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */,
|
27F97369246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift */,
|
||||||
D23A900826125FFB007E14CE /* GetContactBehavior.swift */,
|
D23A900826125FFB007E14CE /* GetContactBehavior.swift */,
|
||||||
@ -1475,11 +1487,11 @@
|
|||||||
path = OneColumn;
|
path = OneColumn;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
7199C8142A4F3A40001568B7 /* Accessibility */ = {
|
582272082B1FC53E00F75BAE /* Accessibility */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7199C8152A4F3A64001568B7 /* AccessibilityHandler.swift */,
|
582272092B1FC55F00F75BAE /* AccessibilityHandler.swift */,
|
||||||
71BE969D2AD96BE6000B5DB7 /* RotorHandler.swift */,
|
5822720A2B1FC55F00F75BAE /* RotorHandler.swift */,
|
||||||
);
|
);
|
||||||
path = Accessibility;
|
path = Accessibility;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1596,6 +1608,7 @@
|
|||||||
D202AFE2242A5F1400E5BEDF /* Extensions */ = {
|
D202AFE2242A5F1400E5BEDF /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
5870636E2ACF238E00CA18D5 /* ReadableDecodingErrors.swift */,
|
||||||
D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift */,
|
D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift */,
|
||||||
0A209CD223A7E2810068F8B0 /* UIStackViewAlignment+Extension.swift */,
|
0A209CD223A7E2810068F8B0 /* UIStackViewAlignment+Extension.swift */,
|
||||||
D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */,
|
D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */,
|
||||||
@ -2009,7 +2022,7 @@
|
|||||||
D29DF0CE21E404D4003B2FB9 /* MVMCoreUI */ = {
|
D29DF0CE21E404D4003B2FB9 /* MVMCoreUI */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7199C8142A4F3A40001568B7 /* Accessibility */,
|
582272082B1FC53E00F75BAE /* Accessibility */,
|
||||||
01F2C1FC27C81F9700DC3D36 /* Managers */,
|
01F2C1FC27C81F9700DC3D36 /* Managers */,
|
||||||
D2ED27D8254B0C1F00A1C293 /* Alerts */,
|
D2ED27D8254B0C1F00A1C293 /* Alerts */,
|
||||||
27F973512466071600CAB5C5 /* Behaviors */,
|
27F973512466071600CAB5C5 /* Behaviors */,
|
||||||
@ -2742,6 +2755,7 @@
|
|||||||
011D95A3240453F8000E3791 /* RuleRegexModel.swift in Sources */,
|
011D95A3240453F8000E3791 /* RuleRegexModel.swift in Sources */,
|
||||||
D2E2A98323D8B32D000B42E6 /* EyebrowHeadlineBodyLinkModel.swift in Sources */,
|
D2E2A98323D8B32D000B42E6 /* EyebrowHeadlineBodyLinkModel.swift in Sources */,
|
||||||
012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */,
|
012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */,
|
||||||
|
5822720B2B1FC55F00F75BAE /* AccessibilityHandler.swift in Sources */,
|
||||||
BB6C6AC1242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift in Sources */,
|
BB6C6AC1242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift in Sources */,
|
||||||
32D2609724C19E2100B56344 /* LockupsPlanSMLXLModel.swift in Sources */,
|
32D2609724C19E2100B56344 /* LockupsPlanSMLXLModel.swift in Sources */,
|
||||||
EA985C872981AB0F00F2FF2E /* VDS-Tilelet+Codable.swift in Sources */,
|
EA985C872981AB0F00F2FF2E /* VDS-Tilelet+Codable.swift in Sources */,
|
||||||
@ -2762,7 +2776,7 @@
|
|||||||
01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */,
|
01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */,
|
||||||
D2A92884241ACB25004E01C6 /* ProgrammaticScrollViewController.swift in Sources */,
|
D2A92884241ACB25004E01C6 /* ProgrammaticScrollViewController.swift in Sources */,
|
||||||
EA985C3E2970938F00F2FF2E /* Tilelet.swift in Sources */,
|
EA985C3E2970938F00F2FF2E /* Tilelet.swift in Sources */,
|
||||||
D23A90002612347A007E14CE /* PageBehaviorHandlerModelProtocol.swift in Sources */,
|
D23A90002612347A007E14CE /* PageBehaviorContainerModelProtocol.swift in Sources */,
|
||||||
EAA78020290081320057DFDF /* VDSMoleculeViewProtocol.swift in Sources */,
|
EAA78020290081320057DFDF /* VDSMoleculeViewProtocol.swift in Sources */,
|
||||||
0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */,
|
0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */,
|
||||||
D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */,
|
D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */,
|
||||||
@ -2793,6 +2807,7 @@
|
|||||||
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */,
|
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */,
|
||||||
AF8118302AB39B0900FAD1BA /* RawRepresentableCodable.swift in Sources */,
|
AF8118302AB39B0900FAD1BA /* RawRepresentableCodable.swift in Sources */,
|
||||||
94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */,
|
94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */,
|
||||||
|
5846ABF62B4762A600FA6C76 /* PollingBehaviorModel.swift in Sources */,
|
||||||
D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */,
|
D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */,
|
||||||
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */,
|
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */,
|
||||||
525239C02407BCFF00454969 /* ListTwoColumnPriceDetailsModel.swift in Sources */,
|
525239C02407BCFF00454969 /* ListTwoColumnPriceDetailsModel.swift in Sources */,
|
||||||
@ -2808,6 +2823,7 @@
|
|||||||
0A0FEC7425D42A5E00AF2548 /* BaseItemPickerEntryField.swift in Sources */,
|
0A0FEC7425D42A5E00AF2548 /* BaseItemPickerEntryField.swift in Sources */,
|
||||||
D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */,
|
D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */,
|
||||||
D23A8FF82612308D007E14CE /* PageBehaviorProtocolRequirer.swift in Sources */,
|
D23A8FF82612308D007E14CE /* PageBehaviorProtocolRequirer.swift in Sources */,
|
||||||
|
5870636F2ACF238E00CA18D5 /* ReadableDecodingErrors.swift in Sources */,
|
||||||
94C2D9A723872DA90006CF46 /* LabelAttributeColorModel.swift in Sources */,
|
94C2D9A723872DA90006CF46 /* LabelAttributeColorModel.swift in Sources */,
|
||||||
943820842432382400B43AF3 /* WebView.swift in Sources */,
|
943820842432382400B43AF3 /* WebView.swift in Sources */,
|
||||||
0103B84E23D7E33A009C315C /* HeadlineBodyToggleModel.swift in Sources */,
|
0103B84E23D7E33A009C315C /* HeadlineBodyToggleModel.swift in Sources */,
|
||||||
@ -2876,7 +2892,6 @@
|
|||||||
0A7ECC702441001C00C828E8 /* UIToolbar+Extension.swift in Sources */,
|
0A7ECC702441001C00C828E8 /* UIToolbar+Extension.swift in Sources */,
|
||||||
D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */,
|
D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */,
|
||||||
AA3561AC24C9684400452EB1 /* ListRightVariableRightCaretAllTextAndLinksModel.swift in Sources */,
|
AA3561AC24C9684400452EB1 /* ListRightVariableRightCaretAllTextAndLinksModel.swift in Sources */,
|
||||||
7199C8162A4F3A64001568B7 /* AccessibilityHandler.swift in Sources */,
|
|
||||||
D209234F244F77FD0044AD09 /* ThreeLayerCenterTemplate.swift in Sources */,
|
D209234F244F77FD0044AD09 /* ThreeLayerCenterTemplate.swift in Sources */,
|
||||||
525019E52406852100EED91C /* ListFourColumnDataUsageDividerModel.swift in Sources */,
|
525019E52406852100EED91C /* ListFourColumnDataUsageDividerModel.swift in Sources */,
|
||||||
32D2609624C19E2100B56344 /* LockupsPlanSMLXL.swift in Sources */,
|
32D2609624C19E2100B56344 /* LockupsPlanSMLXL.swift in Sources */,
|
||||||
@ -2960,6 +2975,7 @@
|
|||||||
EA7D81642B2BABCB00D29F9E /* TooltipModel.swift in Sources */,
|
EA7D81642B2BABCB00D29F9E /* TooltipModel.swift in Sources */,
|
||||||
AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */,
|
AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */,
|
||||||
AFA4935729EE3DCC001A9663 /* AlertDelegateProtocol.swift in Sources */,
|
AFA4935729EE3DCC001A9663 /* AlertDelegateProtocol.swift in Sources */,
|
||||||
|
58A9DD7D2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift in Sources */,
|
||||||
D264FAA3243E632F00D98315 /* ProgrammaticCollectionViewController.swift in Sources */,
|
D264FAA3243E632F00D98315 /* ProgrammaticCollectionViewController.swift in Sources */,
|
||||||
D29DF27A21E7A533003B2FB9 /* MVMCoreUISession.m in Sources */,
|
D29DF27A21E7A533003B2FB9 /* MVMCoreUISession.m in Sources */,
|
||||||
27F9736A246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift in Sources */,
|
27F9736A246750BE00CAB5C5 /* ScreenBrightnessModifierBehavior.swift in Sources */,
|
||||||
@ -3009,6 +3025,7 @@
|
|||||||
8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */,
|
8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */,
|
||||||
AFA4932229E5EF2E001A9663 /* NotificationHandler.swift in Sources */,
|
AFA4932229E5EF2E001A9663 /* NotificationHandler.swift in Sources */,
|
||||||
BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */,
|
BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */,
|
||||||
|
5822720C2B1FC55F00F75BAE /* RotorHandler.swift in Sources */,
|
||||||
AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */,
|
AA104ADC244734EA004D2810 /* HeadersH1LandingPageHeaderModel.swift in Sources */,
|
||||||
BBAA4F03243D8E3B005AAD5F /* RadioBoxes.swift in Sources */,
|
BBAA4F03243D8E3B005AAD5F /* RadioBoxes.swift in Sources */,
|
||||||
323AC96A24C837F000F8E4C4 /* ListThreeColumnBillChangesModel.swift in Sources */,
|
323AC96A24C837F000F8E4C4 /* ListThreeColumnBillChangesModel.swift in Sources */,
|
||||||
@ -3053,7 +3070,6 @@
|
|||||||
D29C559325C0992D0082E7D6 /* VideoModel.swift in Sources */,
|
D29C559325C0992D0082E7D6 /* VideoModel.swift in Sources */,
|
||||||
D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */,
|
D264FAA5243F66A500D98315 /* CollectionTemplateItemProtocol.swift in Sources */,
|
||||||
D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */,
|
D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */,
|
||||||
71BE969E2AD96BE6000B5DB7 /* RotorHandler.swift in Sources */,
|
|
||||||
AA71AD3E24A32FCE00ACA76F /* HeadersH2LinkModel.swift in Sources */,
|
AA71AD3E24A32FCE00ACA76F /* HeadersH2LinkModel.swift in Sources */,
|
||||||
8DD1E36E243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift in Sources */,
|
8DD1E36E243B3CFB00D8F2DF /* ListThreeColumnInternationalDataModel.swift in Sources */,
|
||||||
D243859923A16B1800332775 /* Container.swift in Sources */,
|
D243859923A16B1800332775 /* Container.swift in Sources */,
|
||||||
|
|||||||
@ -33,6 +33,7 @@ import Foundation
|
|||||||
|
|
||||||
super.set(with: model, delegateObject, additionalData)
|
super.set(with: model, delegateObject, additionalData)
|
||||||
FormValidator.setupValidation(for: castModel, delegate: delegateObject?.formHolderDelegate)
|
FormValidator.setupValidation(for: castModel, delegate: delegateObject?.formHolderDelegate)
|
||||||
|
setState()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setState() {
|
public func setState() {
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
open class ImageButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol {
|
open class ImageButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol, ParentMoleculeModelProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -29,6 +29,14 @@ open class ImageButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGro
|
|||||||
|
|
||||||
public var updateUI: ActionBlock?
|
public var updateUI: ActionBlock?
|
||||||
|
|
||||||
|
public var children: [MoleculeModelProtocol] {
|
||||||
|
[image].compactMap({$0})
|
||||||
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &image, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
public init(image: ImageViewModel?, action: ActionModelProtocol) {
|
public init(image: ImageViewModel?, action: ActionModelProtocol) {
|
||||||
self.image = image
|
self.image = image
|
||||||
self.action = action
|
self.action = action
|
||||||
|
|||||||
63
MVMCoreUI/Atomic/Extensions/ReadableDecodingErrors.swift
Normal file
63
MVMCoreUI/Atomic/Extensions/ReadableDecodingErrors.swift
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,8 +25,6 @@ extension VDS.Tabs.Overflow: Codable {}
|
|||||||
extension VDS.Tabs.Size: Codable {}
|
extension VDS.Tabs.Size: Codable {}
|
||||||
extension VDS.TextLink.Size: Codable {}
|
extension VDS.TextLink.Size: Codable {}
|
||||||
extension VDS.TextLinkCaret.IconPosition: Codable {}
|
extension VDS.TextLinkCaret.IconPosition: Codable {}
|
||||||
extension VDS.TileContainer.BackgroundColor: Codable {}
|
|
||||||
extension VDS.TileContainer.Padding: Codable {}
|
|
||||||
extension VDS.TileContainer.AspectRatio: Codable {}
|
extension VDS.TileContainer.AspectRatio: Codable {}
|
||||||
extension VDS.TitleLockup.TextAlignment: Codable {}
|
extension VDS.TitleLockup.TextAlignment: Codable {}
|
||||||
extension VDS.Tooltip.FillColor: Codable {}
|
extension VDS.Tooltip.FillColor: Codable {}
|
||||||
@ -62,3 +60,75 @@ extension DecodableDefault {
|
|||||||
public typealias BlackColor = Wrapper<Sources.BlackColor>
|
public typealias BlackColor = Wrapper<Sources.BlackColor>
|
||||||
public typealias Surface = Wrapper<Sources.Surface>
|
public typealias Surface = Wrapper<Sources.Surface>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension VDS.TileContainer.BackgroundColor: Codable {
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.singleValueContainer()
|
||||||
|
switch self {
|
||||||
|
case .custom(let value):
|
||||||
|
try container.encode(value)
|
||||||
|
default:
|
||||||
|
try container.encode(String(reflecting: self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init from decoder to handle the decoding based on the type
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.singleValueContainer()
|
||||||
|
let type = try container.decode(String.self)
|
||||||
|
switch type {
|
||||||
|
case "primary":
|
||||||
|
self = .primary
|
||||||
|
case "secondary":
|
||||||
|
self = .secondary
|
||||||
|
case "white":
|
||||||
|
self = .white
|
||||||
|
case "black":
|
||||||
|
self = .black
|
||||||
|
default:
|
||||||
|
self = .custom(type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VDS.TileContainer.Padding: Codable {
|
||||||
|
enum PaddingError: Error {
|
||||||
|
case valueNotFound(type: String)
|
||||||
|
}
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.singleValueContainer()
|
||||||
|
switch self {
|
||||||
|
case .custom(let value):
|
||||||
|
try container.encode(value)
|
||||||
|
default:
|
||||||
|
try container.encode(String(reflecting: self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init from decoder to handle the decoding based on the type
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.singleValueContainer()
|
||||||
|
do {
|
||||||
|
let type = try container.decode(String.self)
|
||||||
|
switch type {
|
||||||
|
case "padding2X":
|
||||||
|
self = .padding2X
|
||||||
|
case "padding4X":
|
||||||
|
self = .padding4X
|
||||||
|
case "padding6X":
|
||||||
|
self = .padding6X
|
||||||
|
case "padding8X":
|
||||||
|
self = .padding8X
|
||||||
|
case "padding12X":
|
||||||
|
self = .padding12X
|
||||||
|
default:
|
||||||
|
throw PaddingError.valueNotFound(type: type)
|
||||||
|
}
|
||||||
|
} catch PaddingError.valueNotFound(let type) {
|
||||||
|
throw PaddingError.valueNotFound(type: type)
|
||||||
|
} catch {
|
||||||
|
let type = try container.decode(CGFloat.self)
|
||||||
|
self = .custom(type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -20,6 +20,11 @@ public class HeadersH1ButtonModel: HeaderModel, MoleculeModelProtocol, ParentMol
|
|||||||
[headlineBody, buttons]
|
[headlineBody, buttons]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|
||||||
|
|| replaceChildMolecule(at: &buttons, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -24,6 +24,15 @@ public class HeadersH1LandingPageHeaderModel: HeaderModel, MoleculeModelProtocol
|
|||||||
[headline, headline2, subHeadline, body, link, buttons]
|
[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
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -20,6 +20,10 @@ public class HeadersH1NoButtonsBodyTextModel: HeaderModel, MoleculeModelProtocol
|
|||||||
[headlineBody]
|
[headlineBody]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -22,6 +22,11 @@ public class HeadersH2ButtonsModel: HeaderModel, MoleculeModelProtocol, ParentMo
|
|||||||
[headlineBody, buttons]
|
[headlineBody, buttons]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|
||||||
|
|| replaceChildMolecule(at: &buttons, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -19,6 +19,11 @@ public class HeadersH2CaretLinkModel: HeaderModel, MoleculeModelProtocol, Parent
|
|||||||
[headlineBody, caretLink]
|
[headlineBody, caretLink]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|
||||||
|
|| replaceChildMolecule(at: &caretLink, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -21,6 +21,11 @@ public class HeadersH2LinkModel: HeaderModel, MoleculeModelProtocol, ParentMolec
|
|||||||
[headlineBody, link]
|
[headlineBody, link]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|
||||||
|
|| replaceChildMolecule(at: &link, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -21,6 +21,10 @@ public class HeadersH2NoButtonsBodyTextModel: HeaderModel, MoleculeModelProtocol
|
|||||||
[headlineBody]
|
[headlineBody]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -25,6 +25,17 @@ public class HeadersH2PricingTwoRowsModel: HeaderModel, MoleculeModelProtocol, P
|
|||||||
[headline, body, subBody, body2, subBody2, body3, subBody3].compactMap({$0})
|
[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
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -22,6 +22,11 @@ public class HeadersH2TinyButtonModel: HeaderModel, MoleculeModelProtocol, Paren
|
|||||||
[headlineBody, button]
|
[headlineBody, button]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|
||||||
|
|| replaceChildMolecule(at: &button, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -20,6 +20,11 @@ open class ListLeftVariableCheckboxBodyTextModel: ListItemModel, MoleculeModelPr
|
|||||||
[checkbox, headlineBody]
|
[checkbox, headlineBody]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &checkbox, with: molecule)
|
||||||
|
|| replaceChildMolecule(at: &headlineBody, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -20,6 +20,11 @@ public class ListLeftVariableIconAllTextLinksModel: ListItemModel, MoleculeModel
|
|||||||
return [image, eyebrowHeadlineBodyLink]
|
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
|
// MARK: - Method
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -21,6 +21,12 @@ public class ListLeftVariableIconWithRightCaretAllTextLinksModel: ListItemModel,
|
|||||||
return [image, eyebrowHeadlineBodyLink, rightLabel]
|
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
|
// MARK: - Methods
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
|
|||||||
@ -21,6 +21,12 @@ public class ListLeftVariableIconWithRightCaretBodyTextModel: ListItemModel, Par
|
|||||||
[image, headlineBody, rightLabel]
|
[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
|
// MARK: - Methods
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
|
|||||||
@ -21,6 +21,12 @@ public class ListLeftVariableIconWithRightCaretModel: ListItemModel, ParentMolec
|
|||||||
return [image, leftLabel, rightLabel]
|
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
|
// MARK: - Methods
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
open class ListLeftVariableRadioButtonBodyTextModel: ListItemModel, MoleculeModelProtocol {
|
open class ListLeftVariableRadioButtonBodyTextModel: ListItemModel, ParentMoleculeModelProtocol {
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
@ -16,6 +16,15 @@ open class ListLeftVariableRadioButtonBodyTextModel: ListItemModel, MoleculeMode
|
|||||||
public var radioButton: RadioButtonModel
|
public var radioButton: RadioButtonModel
|
||||||
public var headlineBody: HeadlineBodyModel
|
public var headlineBody: HeadlineBodyModel
|
||||||
|
|
||||||
|
public var children: [MoleculeModelProtocol] {
|
||||||
|
[radioButton, headlineBody]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &radioButton, with: replacementMolecule)
|
||||||
|
|| replaceChildMolecule(at: &headlineBody, with: replacementMolecule)
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
|
|||||||
@ -39,6 +39,10 @@ public class ListOneColumnFullWidthTextBodyTextModel: ListItemModel, MoleculeMod
|
|||||||
return [headlineBody]
|
return [headlineBody]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &headlineBody, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Keys
|
// MARK: - Keys
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -40,6 +40,11 @@ public class ListRightVariableButtonAllTextAndLinksModel: ListItemModel, Molecul
|
|||||||
return [button, eyebrowHeadlineBodyLink]
|
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
|
// MARK: - Keys
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -20,6 +20,11 @@ public class ListRightVariableRightCaretAllTextAndLinksModel: ListItemModel, Par
|
|||||||
[rightLabel, eyebrowHeadlineBodyLink]
|
[rightLabel, eyebrowHeadlineBodyLink]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &rightLabel, with: molecule)
|
||||||
|
|| replaceChildMolecule(at: &eyebrowHeadlineBodyLink, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
// MARK: - Methods
|
// MARK: - Methods
|
||||||
//-----------------------------------------------------
|
//-----------------------------------------------------
|
||||||
|
|||||||
@ -34,6 +34,12 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco
|
|||||||
[eyebrow, title, subTitle].compactMap { (molecule: MoleculeModelProtocol?) in molecule }
|
[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
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -22,6 +22,11 @@ public class ListOneColumnFullWidthTextDividerSubsectionModel: ListItemModel, Mo
|
|||||||
[headline, body].compactMap({$0})
|
[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
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -22,6 +22,11 @@ public class ListOneColumnTextWithWhitespaceDividerShortModel: ListItemModel, Mo
|
|||||||
[headline, body].compactMap({$0})
|
[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
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -22,6 +22,11 @@ public class ListOneColumnTextWithWhitespaceDividerTallModel: ListItemModel, Mol
|
|||||||
[headline, body].compactMap({$0})
|
[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
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import VDS
|
|||||||
if let delegate {
|
if let delegate {
|
||||||
onTabDidSelect = { [weak self] index in
|
onTabDidSelect = { [weak self] index in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
viewModel.selectedIndex = index
|
||||||
delegate.didSelectItem(.init(row: index, section: 0), tabs: self)
|
delegate.didSelectItem(.init(row: index, section: 0), tabs: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,11 @@ public class TwoButtonViewModel: ParentMoleculeModelProtocol {
|
|||||||
|
|
||||||
public var children: [MoleculeModelProtocol] { [primaryButton, secondaryButton].compactMap { $0 } }
|
public var children: [MoleculeModelProtocol] { [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
|
// MARK: - Keys
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
public class TabsListItemModel: ListItemModel, MoleculeModelProtocol {
|
public class TabsListItemModel: ListItemModel, ParentMoleculeModelProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -19,6 +19,23 @@ public class TabsListItemModel: ListItemModel, MoleculeModelProtocol {
|
|||||||
var molecules: [[ListItemModelProtocol & MoleculeModelProtocol]]
|
var molecules: [[ListItemModelProtocol & MoleculeModelProtocol]]
|
||||||
private var addedMolecules: [ListItemModelProtocol & MoleculeModelProtocol]?
|
private var addedMolecules: [ListItemModelProtocol & MoleculeModelProtocol]?
|
||||||
|
|
||||||
|
public var children: [MoleculeModelProtocol] {
|
||||||
|
return molecules.flatMap { $0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
guard let replacementMolecule = replacementMolecule as? ListItemModelProtocol & MoleculeModelProtocol else { return false }
|
||||||
|
for (tabIndex, _) in molecules.enumerated() {
|
||||||
|
for (elementIndex, _) in molecules[tabIndex].enumerated() {
|
||||||
|
if molecules[tabIndex][elementIndex].id == replacementMolecule.id {
|
||||||
|
molecules[tabIndex][elementIndex] = replacementMolecule
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Keys
|
// MARK: - Keys
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -18,10 +18,19 @@ public class CornerLabelsModel: ParentMoleculeModelProtocol {
|
|||||||
public var bottomLeftLabel: LabelModel?
|
public var bottomLeftLabel: LabelModel?
|
||||||
public var bottomRightLabel: LabelModel?
|
public var bottomRightLabel: LabelModel?
|
||||||
public var molecule: MoleculeModelProtocol?
|
public var molecule: MoleculeModelProtocol?
|
||||||
|
|
||||||
public var children: [MoleculeModelProtocol] {
|
public var children: [MoleculeModelProtocol] {
|
||||||
[molecule, topLeftLabel, topRightLabel, bottomLeftLabel, bottomRightLabel].compactMap { $0 }
|
[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?) {
|
public init(with molecule: MoleculeModelProtocol?) {
|
||||||
self.molecule = molecule
|
self.molecule = molecule
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ open class ModuleMolecule: Container {
|
|||||||
super.set(with: model, delegateObject, additionalData)
|
super.set(with: model, delegateObject, additionalData)
|
||||||
|
|
||||||
guard let moduleMoleculeModel = model as? ModuleMoleculeModel,
|
guard let moduleMoleculeModel = model as? ModuleMoleculeModel,
|
||||||
let moduleModel = delegateObject?.moleculeDelegate?.getModuleWithName(moduleMoleculeModel.moduleName) else {
|
let moduleModel = try? delegateObject?.moleculeDelegate?.getModuleWithName(moduleMoleculeModel.moduleName) else {
|
||||||
// Critical error
|
// Critical error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ open class ModuleMolecule: Container {
|
|||||||
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||||
|
|
||||||
guard let moduleMolecule = model as? ModuleMoleculeModel,
|
guard let moduleMolecule = model as? ModuleMoleculeModel,
|
||||||
let moduleModel = delegateObject?.moleculeDelegate?.getModuleWithName(moduleMolecule.moduleName),
|
let moduleModel = try? delegateObject?.moleculeDelegate?.getModuleWithName(moduleMolecule.moduleName),
|
||||||
let classType = ModelRegistry.getMoleculeClass(moduleModel),
|
let classType = ModelRegistry.getMoleculeClass(moduleModel),
|
||||||
let height = classType.estimatedHeight(with: moduleModel, delegateObject) else {
|
let height = classType.estimatedHeight(with: moduleModel, delegateObject) else {
|
||||||
// Critical error
|
// Critical error
|
||||||
@ -60,7 +60,7 @@ open class ModuleMolecule: Container {
|
|||||||
|
|
||||||
public override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
public override class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||||
guard let moduleMolecule = model as? ModuleMoleculeModel,
|
guard let moduleMolecule = model as? ModuleMoleculeModel,
|
||||||
let moduleModel = delegateObject?.moleculeDelegate?.getModuleWithName(moduleMolecule.moduleName),
|
let moduleModel = try? delegateObject?.moleculeDelegate?.getModuleWithName(moduleMolecule.moduleName),
|
||||||
let classType = ModelRegistry.getMoleculeClass(moduleModel),
|
let classType = ModelRegistry.getMoleculeClass(moduleModel),
|
||||||
let name = classType.nameForReuse(with: moduleModel, delegateObject) else {
|
let name = classType.nameForReuse(with: moduleModel, delegateObject) else {
|
||||||
// Critical error
|
// Critical error
|
||||||
@ -72,7 +72,7 @@ open class ModuleMolecule: Container {
|
|||||||
public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
public override class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||||
|
|
||||||
guard let moduleName = (model as? ModuleMoleculeModel)?.moduleName,
|
guard let moduleName = (model as? ModuleMoleculeModel)?.moduleName,
|
||||||
let _ = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else {
|
let _ = try? delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else {
|
||||||
if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), code: CoreUIErrorCode.ErrorCodeModuleMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) {
|
if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), code: CoreUIErrorCode.ErrorCodeModuleMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) {
|
||||||
error?.pointee = errorObject
|
error?.pointee = errorObject
|
||||||
MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject)
|
MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject)
|
||||||
|
|||||||
@ -20,6 +20,10 @@ open class MoleculeContainerModel: ContainerModel, MoleculeContainerModelProtoco
|
|||||||
return [molecule]
|
return [molecule]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &self.molecule, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Codec
|
// MARK: - Codec
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -16,5 +16,10 @@ public extension MoleculeContainerModelProtocol {
|
|||||||
var children: [MoleculeModelProtocol] {
|
var children: [MoleculeModelProtocol] {
|
||||||
return [molecule]
|
return [molecule]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension MoleculeContainerModelProtocol where Self: AnyObject {
|
||||||
|
mutating func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &molecule, with: replacementMolecule)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,13 @@ public class EyebrowHeadlineBodyLinkModel: MoleculeModelProtocol, ParentMolecule
|
|||||||
[eyebrow, headline, body, link].compactMap { (molecule: MoleculeModelProtocol?) in molecule }
|
[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
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -24,13 +24,9 @@
|
|||||||
[headline, body].compactMap { $0 }
|
[headline, body].compactMap { $0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) -> Bool {
|
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
return [
|
return try replaceChildMolecule(at:&headline, with: replacementMolecule)
|
||||||
\HeadlineBodyModel.headline,
|
|| replaceChildMolecule(at:&body, with: replacementMolecule)
|
||||||
\HeadlineBodyModel.body,
|
|
||||||
].contains {
|
|
||||||
replaceChildMolecule(on: self, keyPath: $0, replacementMolecule: replacementMolecule)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -181,6 +181,10 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||||
|
return (model as? CarouselModel)?.height ?? 80
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - JSON Setters
|
// MARK: - JSON Setters
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -179,4 +179,8 @@ extension CarouselModel {
|
|||||||
return molecules
|
return molecules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(in: &molecules, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,19 +21,6 @@
|
|||||||
public var spacing: CGFloat = Padding.Four
|
public var spacing: CGFloat = Padding.Four
|
||||||
public var useStackSpacingBeforeFirstItem = false
|
public var useStackSpacingBeforeFirstItem = false
|
||||||
|
|
||||||
public var children: [MoleculeModelProtocol] {
|
|
||||||
return molecules
|
|
||||||
}
|
|
||||||
|
|
||||||
public func replaceChildMolecule(with replacementMolecule: MoleculeModelProtocol) -> Bool {
|
|
||||||
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
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -16,7 +16,12 @@ public protocol StackModelProtocol: ParentMoleculeModelProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension StackModelProtocol {
|
extension StackModelProtocol {
|
||||||
|
|
||||||
public var children: [MoleculeModelProtocol] { return molecules }
|
public var children: [MoleculeModelProtocol] { return molecules }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension StackModelProtocol where Self: AnyObject {
|
||||||
|
|
||||||
|
public mutating func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(in: &molecules, with: molecule)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,8 +68,14 @@ public extension Array where Element == MoleculeModelProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func depthFirstTraverse(options: TreeTraversalOptions, depth: Int, onVisit: (Int, MoleculeModelProtocol, inout Bool) -> Void) {
|
func depthFirstTraverse(options: TreeTraversalOptions, depth: Int, onVisit: (Int, MoleculeModelProtocol, inout Bool) -> Void) {
|
||||||
forEach { (molecule) in
|
var shouldStop = false
|
||||||
molecule.depthFirstTraverse(options: options, depth: depth, onVisit: onVisit)
|
let stopIntercept = { depth, molecule, stop in
|
||||||
|
onVisit(depth, molecule, &shouldStop)
|
||||||
|
stop = shouldStop
|
||||||
|
}
|
||||||
|
for molecule in self {
|
||||||
|
molecule.depthFirstTraverse(options: options, depth: depth, onVisit: stopIntercept)
|
||||||
|
if shouldStop { break }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,48 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public protocol ParentMoleculeModelProtocol: MoleculeModelProtocol, AnyObject {
|
public protocol ParentModelProtocol: MoleculeTreeTraversalProtocol {
|
||||||
|
|
||||||
|
/// Returns the direct children of this component. (Does not recurse.)
|
||||||
var children: [MoleculeModelProtocol] { get }
|
var children: [MoleculeModelProtocol] { get }
|
||||||
|
|
||||||
func replaceChildMolecule(with molecule: MoleculeModelProtocol) -> Bool
|
/// Method for replacing surface level children. (Does not recurse.)
|
||||||
|
mutating func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension ParentModelProtocol where Self: AnyObject {
|
||||||
|
|
||||||
|
/// 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 a single molecules with type and optionality checks.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper for replacing a molecule in place within an array. Note the "in".
|
||||||
|
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 {
|
public extension ParentMoleculeModelProtocol {
|
||||||
@ -37,43 +74,27 @@ public extension ParentMoleculeModelProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func depthFirstTraverse(options: TreeTraversalOptions, depth: Int, onVisit: (Int, MoleculeModelProtocol, inout Bool)->Void) {
|
func depthFirstTraverse(options: TreeTraversalOptions, depth: Int, onVisit: (Int, MoleculeModelProtocol, inout Bool)->Void) {
|
||||||
var stop = false
|
var shouldStop = false
|
||||||
if (options == .parentFirst) {
|
if (options == .parentFirst) {
|
||||||
onVisit(depth, self, &stop)
|
onVisit(depth, self, &shouldStop)
|
||||||
guard !stop else { return }
|
guard !shouldStop else { return }
|
||||||
|
}
|
||||||
|
let stopIntercept: (Int, MoleculeModelProtocol, inout Bool)->Void = { depth, model, stop in
|
||||||
|
onVisit(depth, model, &shouldStop)
|
||||||
|
stop = shouldStop
|
||||||
}
|
}
|
||||||
for child in children {
|
for child in children {
|
||||||
if let additionalParent = child as? ParentMoleculeModelProtocol {
|
if let additionalParent = child as? ParentMoleculeModelProtocol {
|
||||||
// Safety net to make sure the ParentMoleculeModelProtocol's method extension is called over the base MoleculeModelProtocol.
|
// Safety net to make sure the ParentMoleculeModelProtocol's method extension is called over the base MoleculeModelProtocol.
|
||||||
additionalParent.depthFirstTraverse(options: options, depth: depth + 1, onVisit: onVisit)
|
additionalParent.depthFirstTraverse(options: options, depth: depth + 1, onVisit: stopIntercept)
|
||||||
} else {
|
} else {
|
||||||
child.depthFirstTraverse(options: options, depth: depth + 1, onVisit: onVisit)
|
child.depthFirstTraverse(options: options, depth: depth + 1, onVisit: stopIntercept)
|
||||||
}
|
}
|
||||||
guard !stop else { return }
|
guard !shouldStop else { return }
|
||||||
}
|
}
|
||||||
if (options == .childFirst) {
|
if (options == .childFirst) {
|
||||||
onVisit(depth, self, &stop)
|
onVisit(depth, self, &shouldStop)
|
||||||
}
|
}
|
||||||
// if options == .leafOnly don't call on self.
|
// 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 on a path.
|
|
||||||
func replaceChildMolecule<P: ParentMoleculeModelProtocol, T: MoleculeModelProtocol>(on target: P, keyPath: ReferenceWritableKeyPath<P, T?>, replacementMolecule: MoleculeModelProtocol) -> Bool {
|
|
||||||
if let currentMolecule = target[keyPath: keyPath], currentMolecule.id == replacementMolecule.id, let newHeadline = replacementMolecule as? T {
|
|
||||||
target[keyPath: keyPath] = newHeadline
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func replaceChildMolecule<P: ParentMoleculeModelProtocol, T: MoleculeModelProtocol>(on target: P, keyPath: ReferenceWritableKeyPath<P, T>, replacementMolecule: MoleculeModelProtocol) -> Bool {
|
|
||||||
if target[keyPath: keyPath].id == replacementMolecule.id, let newHeadline = replacementMolecule as? T {
|
|
||||||
target[keyPath: keyPath] = newHeadline
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,13 +7,17 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
public protocol TemplateModelProtocol: PageModelProtocol, ModelProtocol, MoleculeTreeTraversalProtocol {
|
public protocol TemplateModelProtocol: PageModelProtocol, ModelProtocol, MoleculeTreeTraversalProtocol, ParentModelProtocol {
|
||||||
var template: String { get }
|
var template: String { get }
|
||||||
var rootMolecules: [MoleculeModelProtocol] { get }
|
var rootMolecules: [MoleculeModelProtocol] { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension TemplateModelProtocol {
|
public extension TemplateModelProtocol {
|
||||||
|
|
||||||
|
var children: [MoleculeModelProtocol] {
|
||||||
|
return rootMolecules
|
||||||
|
}
|
||||||
|
|
||||||
var template: String {
|
var template: String {
|
||||||
get { return Self.identifier }
|
get { return Self.identifier }
|
||||||
}
|
}
|
||||||
@ -34,3 +38,31 @@ public extension TemplateModelProtocol {
|
|||||||
return rootMolecules.depthFirstTraverse(options: options, depth: depth, onVisit: onVisit)
|
return rootMolecules.depthFirstTraverse(options: options, depth: depth, onVisit: onVisit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension TemplateModelProtocol {
|
||||||
|
|
||||||
|
/// Recursively finds and replaces the first child matching the replacement molecule id property.
|
||||||
|
mutating func replaceMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
// Attempt root level replacement on the template model first.
|
||||||
|
if try 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 var 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -9,39 +9,49 @@
|
|||||||
|
|
||||||
public protocol MoleculeDelegateProtocol: AnyObject {
|
public protocol MoleculeDelegateProtocol: AnyObject {
|
||||||
|
|
||||||
|
func getTemplateModel() -> TemplateModelProtocol?
|
||||||
|
|
||||||
func getRootMolecules() -> [MoleculeModelProtocol]
|
func getRootMolecules() -> [MoleculeModelProtocol]
|
||||||
|
|
||||||
/// returns a module for the corresponding module name.
|
/// Returns a raw module map for the corresponding module name if the key name exists.
|
||||||
func getModuleWithName(_ name: String?) -> [AnyHashable: Any]?
|
func getModuleWithName(_ name: String?) -> [AnyHashable: Any]?
|
||||||
|
|
||||||
func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol?
|
/// Returns the decoded module for the corresponding module name if the key name exists. Throws if there is a decoding error.
|
||||||
|
func getModuleWithName(_ moleculeName: String) throws -> MoleculeModelProtocol?
|
||||||
|
|
||||||
/// Notifies the delegate that the molecule layout update. Should be called when the layout may change due to an async method. Mainly used for list or collections.
|
/// Notifies the delegate that the molecule layout update. Should be called when the layout may change due to an async method. Mainly used for list or collections.
|
||||||
func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) //optional
|
func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) //optional
|
||||||
|
|
||||||
|
func replaceMoleculeData(_ moleculeModels: [MoleculeModelProtocol])
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MoleculeDelegateProtocol {
|
extension MoleculeDelegateProtocol {
|
||||||
|
|
||||||
|
public func getRootMolecules() -> [MoleculeModelProtocol] {
|
||||||
|
getTemplateModel()?.rootMolecules ?? []
|
||||||
|
}
|
||||||
|
|
||||||
public func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { }
|
public func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { }
|
||||||
|
|
||||||
public func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? {
|
public func getModuleWithName(_ moleculeName: String) throws -> MoleculeModelProtocol? {
|
||||||
let moduleJSON: [AnyHashable: Any]? = getModuleWithName(moleculeName)
|
let moduleJSON: [AnyHashable: Any]? = getModuleWithName(moleculeName)
|
||||||
guard let moduleJSON = moduleJSON as? [String: Any],
|
guard let moduleJSON = moduleJSON as? [String: Any] else { return nil }
|
||||||
let moleculeName = moduleJSON.optionalStringForKey("moleculeName"),
|
|
||||||
let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self)
|
|
||||||
else { return nil }
|
|
||||||
|
|
||||||
do {
|
guard let moleculeName = moduleJSON.optionalStringForKey(KeyMoleculeName),
|
||||||
return try modelType.decode(jsonDict: moduleJSON as [String : Any]) as? MoleculeModelProtocol
|
let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) else {
|
||||||
} catch {
|
throw ModelRegistry.Error.decoderErrorModelNotMapped(identifer: moduleJSON.optionalStringForKey(KeyMoleculeName))
|
||||||
MVMCoreUILoggingHandler.logDebugMessage(withDelegate: "error: \(error)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return try modelType.decode(jsonDict: moduleJSON as [String : Any]) as? MoleculeModelProtocol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MoleculeDelegateProtocol where Self: TemplateProtocol {
|
extension MoleculeDelegateProtocol where Self: TemplateProtocol {
|
||||||
|
|
||||||
|
public func getTemplateModel() -> TemplateModelProtocol? {
|
||||||
|
return templateModel
|
||||||
|
}
|
||||||
|
|
||||||
public func getRootMolecules() -> [MoleculeModelProtocol] {
|
public func getRootMolecules() -> [MoleculeModelProtocol] {
|
||||||
templateModel?.rootMolecules ?? []
|
templateModel?.rootMolecules ?? []
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,7 @@ public extension MoleculeTreeTraversalProtocol {
|
|||||||
|
|
||||||
func printMolecules(options: TreeTraversalOptions = .parentFirst) {
|
func printMolecules(options: TreeTraversalOptions = .parentFirst) {
|
||||||
depthFirstTraverse(options: options, depth: 1) { depth, molecule, stop in
|
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)]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,10 +51,23 @@ public extension MoleculeTreeTraversalProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func replaceMolecule(with replacementMolecule: MoleculeModelProtocol) {
|
func filterMoleculeTree(options: TreeTraversalOptions = .parentFirst, by condition: (MoleculeModelProtocol)->Bool) -> [MoleculeModelProtocol] {
|
||||||
depthFirstTraverse(options: .parentFirst, depth: 0) { depth, molecule, stop in
|
return reduceDepthFirstTraverse(options: options, depth: 0, initialResult: []) { (accumulator, molecule, depth) in
|
||||||
guard let parentMolecule = molecule as? ParentMoleculeModelProtocol else { return }
|
if condition(molecule) {
|
||||||
stop = parentMolecule.replaceChildMolecule(with: replacementMolecule)
|
return accumulator + [molecule]
|
||||||
|
}
|
||||||
|
return accumulator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findFirstMolecule(options: TreeTraversalOptions = .parentFirst, by condition: (MoleculeModelProtocol)->Bool) -> MoleculeModelProtocol? {
|
||||||
|
var foundMolecule: MoleculeModelProtocol?
|
||||||
|
depthFirstTraverse(options: options, depth: 0) { depth, molecule, isDone in
|
||||||
|
isDone = condition(molecule)
|
||||||
|
if isDone {
|
||||||
|
foundMolecule = molecule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foundMolecule
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,25 +28,26 @@ public extension TemplateProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> TemplateModel {
|
||||||
|
try decoder.decode(TemplateModel.self, from: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension TemplateProtocol where Self: PageBehaviorHandlerProtocol, Self: MVMCoreViewControllerProtocol {
|
||||||
|
|
||||||
/// Helper function to do common parsing logic.
|
/// Helper function to do common parsing logic.
|
||||||
func parseTemplate(json: [AnyHashable: Any]?) throws {
|
func parseTemplate(json: [AnyHashable: Any]?) throws {
|
||||||
guard let pageJSON = json else { return }
|
guard let pageJSON = json else { return }
|
||||||
let delegateObject = (self as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
|
let delegateObject = delegateObject?() as? MVMCoreUIDelegateObject
|
||||||
let data = try JSONSerialization.data(withJSONObject: pageJSON)
|
let data = try JSONSerialization.data(withJSONObject: pageJSON)
|
||||||
let decoder = JSONDecoder.create(with: delegateObject)
|
let decoder = JSONDecoder.create(with: delegateObject)
|
||||||
templateModel = try decodeTemplate(using: decoder, from: data)
|
templateModel = try decodeTemplate(using: decoder, from: data)
|
||||||
|
|
||||||
// Add additional required behaviors if applicable.
|
// Add additional required behaviors if applicable.
|
||||||
guard var behaviorHandlerModel = templateModel as? TemplateModelProtocol & PageBehaviorHandlerModelProtocol,
|
guard var pageBehaviorsModel = templateModel as? TemplateModelProtocol & PageBehaviorContainerModelProtocol else { return }
|
||||||
var behaviorHandler = self as? PageBehaviorHandlerProtocol else { return }
|
|
||||||
behaviorHandlerModel.traverseAndAddRequiredBehaviors()
|
|
||||||
behaviorHandler.createBehaviors(for: behaviorHandlerModel, delegateObject: delegateObject)
|
|
||||||
if let viewController = self as? UIViewController {
|
|
||||||
MVMCoreUISession.sharedGlobal()?.applyGlobalBehaviors(to: viewController)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> TemplateModel {
|
pageBehaviorsModel.traverseAndAddRequiredBehaviors()
|
||||||
try decoder.decode(TemplateModel.self, from: data)
|
var behaviorHandler = self
|
||||||
|
behaviorHandler.applyBehaviors(pageBehaviorModel: pageBehaviorsModel, delegateObject: delegateObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,10 @@ import Foundation
|
|||||||
|
|
||||||
public var shouldMaskScreenWhileRecording: Bool?
|
public var shouldMaskScreenWhileRecording: Bool?
|
||||||
|
|
||||||
|
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
|
||||||
|
return try replaceChildMolecule(at: &navigationBar, with: molecule)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -81,13 +81,17 @@
|
|||||||
|
|
||||||
|
|
||||||
open override func handleNewData() {
|
open override func handleNewData() {
|
||||||
topViewOutsideOfScrollArea = templateModel?.anchorHeader ?? false
|
|
||||||
bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false
|
|
||||||
setup()
|
setup()
|
||||||
registerCells()
|
registerCells()
|
||||||
super.handleNewData()
|
super.handleNewData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||||
|
topViewOutsideOfScrollArea = templateModel?.anchorHeader ?? false
|
||||||
|
bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false
|
||||||
|
super.updateUI(for: molecules)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Collection
|
// MARK: - Collection
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -23,6 +23,11 @@
|
|||||||
return super.rootMolecules
|
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
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -26,6 +26,12 @@
|
|||||||
return super.rootMolecules
|
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.
|
/// This template requires content.
|
||||||
func validateModelHasContent() throws {
|
func validateModelHasContent() throws {
|
||||||
if header == nil,
|
if header == nil,
|
||||||
|
|||||||
@ -21,8 +21,8 @@ open class ModalSectionListTemplate: SectionListTemplate {
|
|||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
override open func handleNewData() {
|
override open func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||||
super.handleNewData()
|
super.updateUI(for: molecules)
|
||||||
_ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: { [weak self] _ in
|
_ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, action: { [weak self] _ in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
let closeAction = (self.templateModel as? ModalSectionListTemplateModel)?.closeAction ??
|
let closeAction = (self.templateModel as? ModalSectionListTemplateModel)?.closeAction ??
|
||||||
|
|||||||
@ -82,11 +82,22 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
open override func handleNewData() {
|
open override func handleNewData() {
|
||||||
topViewOutsideOfScrollArea = templateModel?.anchorHeader ?? false
|
|
||||||
bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false
|
|
||||||
setup()
|
setup()
|
||||||
registerWithTable()
|
registerWithTable()
|
||||||
super.handleNewData()
|
super.handleNewData() // Currently stuck as MoleculeListProtocol being called from AddRemoveMoleculesBehaviorModel.
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||||
|
topViewOutsideOfScrollArea = templateModel?.anchorHeader ?? false
|
||||||
|
bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false
|
||||||
|
super.updateUI(for: molecules)
|
||||||
|
|
||||||
|
molecules?.forEach({ molecule in
|
||||||
|
if let index = moleculesInfo?.firstIndex(where: { $0.molecule.id == molecule.id }) {
|
||||||
|
moleculesInfo?[index].molecule = molecule
|
||||||
|
}
|
||||||
|
newData(for: molecule)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func viewDidAppear(_ animated: Bool) {
|
open override func viewDidAppear(_ animated: Bool) {
|
||||||
@ -191,36 +202,44 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func newData(for molecule: MoleculeModelProtocol) {
|
open func newData(for molecule: MoleculeModelProtocol) {
|
||||||
//TODO: expand for header, navigation, etc
|
|
||||||
guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in
|
//Check header and footer if replace happens then return.
|
||||||
if equal(moleculeA: molecule, moleculeB: moleculeInfo.molecule) {
|
if updateHeaderFooterView(topView, with: molecule) ||
|
||||||
return true
|
updateHeaderFooterView(bottomView, with: molecule, isHeader: false) {
|
||||||
} else if let parent = moleculeInfo.molecule as? ParentMoleculeModelProtocol {
|
return
|
||||||
// Get all molecules of the same type for faster check.
|
}
|
||||||
let molecules: [MoleculeModelProtocol] = parent.reduceDepthFirstTraverse(options: .childFirst, depth: 0, initialResult: []) { (accumulator, currentMolecule, depth) in
|
|
||||||
if currentMolecule.moleculeName == molecule.moleculeName {
|
guard let moleculesInfo = moleculesInfo else { return }
|
||||||
return accumulator + [currentMolecule]
|
|
||||||
}
|
let indicies = moleculesInfo.indices.filter({ index -> Bool in
|
||||||
return accumulator
|
return moleculesInfo[index].molecule.findFirstMolecule(by: {
|
||||||
}
|
$0.moleculeName == molecule.moleculeName && equal(moleculeA: molecule, moleculeB: $0)
|
||||||
for moleculeB in molecules {
|
}) != nil
|
||||||
if equal(moleculeA: molecule, moleculeB: moleculeB) {
|
})
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}) else { return }
|
|
||||||
|
|
||||||
// Refresh the cell. (reload loses cell selection)
|
// Refresh the cell. (reload loses cell selection)
|
||||||
let selectedIndex = tableView.indexPathForSelectedRow
|
let selectedIndex = tableView.indexPathForSelectedRow
|
||||||
let indexPath = IndexPath(row: index, section: 0)
|
let indexPaths = indicies.map {
|
||||||
tableView.reloadRows(at: [indexPath], with: .automatic)
|
return IndexPath(row: $0, section: 0)
|
||||||
|
}
|
||||||
|
tableView.reloadRows(at: indexPaths, with: .automatic)
|
||||||
if let selectedIndex = selectedIndex {
|
if let selectedIndex = selectedIndex {
|
||||||
tableView.selectRow(at: selectedIndex, animated: false, scrollPosition: .none)
|
tableView.selectRow(at: selectedIndex, animated: false, scrollPosition: .none)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Helper functions to update header/footer view
|
||||||
|
private func updateHeaderFooterView(_ view: UIView?, with molecule: MoleculeModelProtocol, isHeader: Bool = true) -> Bool {
|
||||||
|
if let updateView = view,
|
||||||
|
let moleculeView = MVMCoreUIUtility.findViews(by: MoleculeViewProtocol.self, views: [updateView]).first(where: { $0.model?.moleculeName == molecule.moleculeName && $0.model?.id == molecule.id }) {
|
||||||
|
updateMoleculeView(moleculeView, from: molecule)
|
||||||
|
//Redraw the header/footer for content update
|
||||||
|
isHeader ? showHeader(nil) : showFooter(nil)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Convenience
|
// MARK: - Convenience
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -293,7 +312,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension MoleculeListTemplate: MoleculeListProtocol {
|
extension MoleculeListTemplate: MoleculeListProtocol {
|
||||||
open func removeMolecules(at indexPaths: [IndexPath], animation: UITableView.RowAnimation?) {
|
public func removeMolecules(at indexPaths: [IndexPath], animation: UITableView.RowAnimation?) {
|
||||||
for (index, indexPath) in indexPaths.sorted().enumerated() {
|
for (index, indexPath) in indexPaths.sorted().enumerated() {
|
||||||
let removeIndex = indexPath.row - index
|
let removeIndex = indexPath.row - index
|
||||||
moleculesInfo?.remove(at: removeIndex)
|
moleculesInfo?.remove(at: removeIndex)
|
||||||
@ -306,7 +325,7 @@ extension MoleculeListTemplate: MoleculeListProtocol {
|
|||||||
view.layoutIfNeeded()
|
view.layoutIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation?) {
|
public func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation?) {
|
||||||
var indexPaths: [IndexPath] = []
|
var indexPaths: [IndexPath] = []
|
||||||
|
|
||||||
for molecule in molecules {
|
for molecule in molecules {
|
||||||
@ -325,7 +344,7 @@ extension MoleculeListTemplate: MoleculeListProtocol {
|
|||||||
self.view.layoutIfNeeded()
|
self.view.layoutIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? {
|
public func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? {
|
||||||
guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in
|
guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in
|
||||||
return equal(moleculeA: molecule, moleculeB: moleculeInfo.molecule)
|
return equal(moleculeA: molecule, moleculeB: moleculeInfo.molecule)
|
||||||
}) else { return nil }
|
}) else { return nil }
|
||||||
|
|||||||
@ -16,7 +16,13 @@
|
|||||||
public var moleculeStack: StackModel
|
public var moleculeStack: StackModel
|
||||||
|
|
||||||
public override var rootMolecules: [MoleculeModelProtocol] {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -26,8 +26,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func handleNewData() {
|
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||||
super.handleNewData()
|
super.updateUI(for: molecules)
|
||||||
heightConstraint?.isActive = true
|
heightConstraint?.isActive = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,12 @@
|
|||||||
[navigationBar, header, footer].compactMap { $0 }
|
[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
|
// MARK: - Init
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -22,6 +22,11 @@
|
|||||||
return super.rootMolecules
|
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
|
// MARK: - Init
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -19,10 +19,10 @@ import UIKit
|
|||||||
try super.parsePageJSON()
|
try super.parsePageJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func handleNewData() {
|
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||||
topViewOutsideOfScroll = templateModel?.anchorHeader ?? false
|
topViewOutsideOfScroll = templateModel?.anchorHeader ?? false
|
||||||
bottomViewOutsideOfScroll = templateModel?.anchorFooter ?? false
|
bottomViewOutsideOfScroll = templateModel?.anchorFooter ?? false
|
||||||
super.handleNewData()
|
super.updateUI(for: molecules)
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func viewForTop() -> UIView? {
|
open override func viewForTop() -> UIView? {
|
||||||
|
|||||||
@ -9,6 +9,6 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public protocol MVMControllerModelProtocol: TemplateModelProtocol, FormHolderModelProtocol, PageBehaviorHandlerModelProtocol {
|
public protocol MVMControllerModelProtocol: TemplateModelProtocol, FormHolderModelProtocol, PageBehaviorContainerModelProtocol {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,8 +63,8 @@ open class ScrollingViewController: ViewController {
|
|||||||
registerForKeyboardNotifications()
|
registerForKeyboardNotifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func handleNewData() {
|
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||||
super.handleNewData()
|
super.updateUI(for: molecules)
|
||||||
// will change scrollView indicatorStyle automatically on the basis of backgroundColor
|
// will change scrollView indicatorStyle automatically on the basis of backgroundColor
|
||||||
var greyScale: CGFloat = 0
|
var greyScale: CGFloat = 0
|
||||||
if view.backgroundColor?.getWhite(&greyScale, alpha: nil) ?? false {
|
if view.backgroundColor?.getWhite(&greyScale, alpha: nil) ?? false {
|
||||||
|
|||||||
@ -112,8 +112,8 @@ import Foundation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func handleNewData() {
|
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||||
super.handleNewData()
|
super.updateUI(for: molecules)
|
||||||
topView?.removeFromSuperview()
|
topView?.removeFromSuperview()
|
||||||
bottomView?.removeFromSuperview()
|
bottomView?.removeFromSuperview()
|
||||||
topView = viewForTop()
|
topView = viewForTop()
|
||||||
|
|||||||
@ -55,8 +55,11 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController, Rotor
|
|||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func handleNewData() {
|
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||||
super.handleNewData()
|
super.updateUI(for: molecules)
|
||||||
|
|
||||||
|
guard molecules == nil else { return }
|
||||||
|
|
||||||
createViewForTableHeader()
|
createViewForTableHeader()
|
||||||
createViewForTableFooter()
|
createViewForTableFooter()
|
||||||
tableView?.reloadData()
|
tableView?.reloadData()
|
||||||
|
|||||||
@ -49,8 +49,8 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController, RotorView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func handleNewData() {
|
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||||
super.handleNewData()
|
super.updateUI(for: molecules)
|
||||||
|
|
||||||
// Removes the views
|
// Removes the views
|
||||||
topView?.removeFromSuperview()
|
topView?.removeFromSuperview()
|
||||||
|
|||||||
@ -38,12 +38,19 @@ import MVMCore
|
|||||||
public var behaviors: [PageBehaviorProtocol]?
|
public var behaviors: [PageBehaviorProtocol]?
|
||||||
|
|
||||||
public var needsUpdateUI = false
|
public var needsUpdateUI = false
|
||||||
private var observingForResponses = false
|
private var observingForResponses: NSObjectProtocol?
|
||||||
private var initialLoadFinished = false
|
private var initialLoadFinished = false
|
||||||
public var previousScreenSize = CGSize.zero
|
public var previousScreenSize = CGSize.zero
|
||||||
|
|
||||||
public var selectedField: UIView?
|
public var selectedField: UIView?
|
||||||
|
|
||||||
|
public var pageUpdateQueue: OperationQueue = {
|
||||||
|
let pageUpdateQueue = OperationQueue()
|
||||||
|
pageUpdateQueue.maxConcurrentOperationCount = 1
|
||||||
|
pageUpdateQueue.qualityOfService = .userInteractive
|
||||||
|
return pageUpdateQueue
|
||||||
|
}()
|
||||||
|
|
||||||
/// Checks if the screen width has changed
|
/// Checks if the screen width has changed
|
||||||
open func screenSizeChanged() -> Bool {
|
open func screenSizeChanged() -> Bool {
|
||||||
!MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1)
|
!MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1)
|
||||||
@ -54,18 +61,17 @@ import MVMCore
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
open func observeForResponseJSONUpdates() {
|
open func observeForResponseJSONUpdates() {
|
||||||
guard !observingForResponses,
|
guard observingForResponses == nil,
|
||||||
(pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0)
|
(pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0)
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
observingForResponses = true
|
observingForResponses = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil, queue: pageUpdateQueue, using: responseJSONUpdated(notification:))
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open func stopObservingForResponseJSONUpdates() {
|
open func stopObservingForResponseJSONUpdates() {
|
||||||
guard observingForResponses else { return }
|
guard let observingForResponses = observingForResponses else { return }
|
||||||
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil)
|
NotificationCenter.default.removeObserver(observingForResponses)
|
||||||
observingForResponses = false
|
self.observingForResponses = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
open func pagesToListenFor() -> [String]? {
|
open func pagesToListenFor() -> [String]? {
|
||||||
@ -74,7 +80,9 @@ import MVMCore
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func modulesToListenFor() -> [String]? {
|
open func modulesToListenFor() -> [String]? {
|
||||||
loadObject?.requestParameters?.allModules()
|
let requestModules = loadObject?.requestParameters?.allModules() ?? []
|
||||||
|
let behaviorModules = behaviors?.flatMap { $0.modulesToListenFor() } ?? []
|
||||||
|
return requestModules + behaviorModules
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc open func responseJSONUpdated(notification: Notification) {
|
@objc open func responseJSONUpdated(notification: Notification) {
|
||||||
@ -109,9 +117,12 @@ import MVMCore
|
|||||||
guard newData else { return }
|
guard newData else { return }
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
// TODO: Parse parsePageJSON modifies the page model on a different thread than
|
||||||
|
// the UI update which could cause discrepancies. Parse should return the resulting
|
||||||
|
// object and assignment should be synchronized on handleNewData(model: ).
|
||||||
try parsePageJSON()
|
try parsePageJSON()
|
||||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
||||||
self.handleNewDataAndUpdateUI()
|
self.handleNewData()
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: pageType))") {
|
if let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: "updateJSON for pageType: \(String(describing: pageType))") {
|
||||||
@ -163,35 +174,8 @@ import MVMCore
|
|||||||
}
|
}
|
||||||
|
|
||||||
func describe(parsingError: Error) -> String {
|
func describe(parsingError: Error) -> String {
|
||||||
if let registryError = parsingError as? ModelRegistry.Error {
|
if let error = parsingError as? HumanReadableDecodingErrorProtocol {
|
||||||
switch (registryError) {
|
return "Error parsing template. \(error.readableDescription)"
|
||||||
case .decoderErrorModelNotMapped(let identifier, let codingKey, let codingPath) where identifier != nil && codingKey != nil && codingPath != nil:
|
|
||||||
return "Error parsing template. Model identifier \"\(identifier!)\" is not mapped for \"\(codingKey!.stringValue)\" @ \(codingPath!.map { return $0.stringValue })"
|
|
||||||
|
|
||||||
case .decoderErrorObjectNotPresent(let codingKey, let codingPath):
|
|
||||||
return "Error parsing template. Required model \"\(codingKey.stringValue)\" was not found @ \(codingPath.map { return $0.stringValue })"
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "Error parsing template. Registry error: \((registryError as NSError).localizedFailureReason ?? registryError.localizedDescription)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let decodingError = parsingError as? DecodingError {
|
|
||||||
switch (decodingError) {
|
|
||||||
case .keyNotFound(let codingKey, let context):
|
|
||||||
return "Error parsing template. Required key \(codingKey.stringValue) was not found @ \(context.codingPath.map { return $0.stringValue })"
|
|
||||||
|
|
||||||
case .valueNotFound(_, let context):
|
|
||||||
return "Error parsing template. Value not found @ \(context.codingPath.map { return $0.stringValue })"
|
|
||||||
|
|
||||||
case .typeMismatch(_, let context):
|
|
||||||
return "Error parsing template. Value type mismatch @ \(context.codingPath.map { return $0.stringValue })"
|
|
||||||
|
|
||||||
case .dataCorrupted(let context):
|
|
||||||
return "Error parsing template. Data corrupted @ \(context.codingPath.map { return $0.stringValue })"
|
|
||||||
|
|
||||||
@unknown default:
|
|
||||||
return "Error parsing template. \((parsingError as NSError).localizedFailureReason ?? parsingError.localizedDescription)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "Error parsing template. \((parsingError as NSError).localizedFailureReason ?? parsingError.localizedDescription)"
|
return "Error parsing template. \((parsingError as NSError).localizedFailureReason ?? parsingError.localizedDescription)"
|
||||||
}
|
}
|
||||||
@ -227,13 +211,6 @@ import MVMCore
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls processNewData and then sets the ui to update with updateView
|
|
||||||
open func handleNewDataAndUpdateUI() {
|
|
||||||
handleNewData()
|
|
||||||
needsUpdateUI = true
|
|
||||||
view.setNeedsLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a legacy navigation model.
|
/// Creates a legacy navigation model.
|
||||||
open func createDefaultLegacyNavigationModel() -> NavigationItemModel {
|
open func createDefaultLegacyNavigationModel() -> NavigationItemModel {
|
||||||
let navigationModel = NavigationItemModel()
|
let navigationModel = NavigationItemModel()
|
||||||
@ -241,7 +218,8 @@ import MVMCore
|
|||||||
return navigationModel
|
return navigationModel
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes any new data. Called after the page is loaded the first time and on response updates for this page,
|
/// Processes any new data. Called after the page is loaded the first time and on response updates for this page, Triggers a render refresh.
|
||||||
|
@MainActor
|
||||||
open func handleNewData() {
|
open func handleNewData() {
|
||||||
if model?.navigationBar == nil {
|
if model?.navigationBar == nil {
|
||||||
let navigationItem = createDefaultLegacyNavigationModel()
|
let navigationItem = createDefaultLegacyNavigationModel()
|
||||||
@ -257,12 +235,27 @@ import MVMCore
|
|||||||
formValidator = FormValidator(rules)
|
formValidator = FormValidator(rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateUI()
|
||||||
|
|
||||||
|
// Notify the manager of new data.
|
||||||
|
// Warning: Some flows cause table reloads. Until the UI update is decoupled, should be after the updateUI.
|
||||||
|
manager?.newDataReceived?(in: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies the latest model to the UI.
|
||||||
|
open func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||||
|
guard molecules == nil else { return }
|
||||||
|
|
||||||
|
executeBehaviors { (behavior: PageMoleculeTransformationBehavior) in
|
||||||
|
behavior.willRender(rootMolecules: getRootMolecules(), delegateObjectIVar)
|
||||||
|
}
|
||||||
|
|
||||||
if let backgroundColor = model?.backgroundColor {
|
if let backgroundColor = model?.backgroundColor {
|
||||||
view.backgroundColor = backgroundColor.uiColor
|
view.backgroundColor = backgroundColor.uiColor
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the manager of new data
|
needsUpdateUI = true
|
||||||
manager?.newDataReceived?(in: self)
|
view.setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func generateMoleculeView(from model: MoleculeModelProtocol) -> MoleculeViewProtocol? {
|
public func generateMoleculeView(from model: MoleculeModelProtocol) -> MoleculeViewProtocol? {
|
||||||
@ -331,7 +324,7 @@ import MVMCore
|
|||||||
initialLoad()
|
initialLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNewDataAndUpdateUI()
|
handleNewData()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func viewDidLayoutSubviews() {
|
open override func viewDidLayoutSubviews() {
|
||||||
@ -511,13 +504,47 @@ import MVMCore
|
|||||||
// MARK: - MoleculeDelegateProtocol
|
// MARK: - MoleculeDelegateProtocol
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
open func getRootMolecules() -> [MoleculeModelProtocol] {
|
open func getTemplateModel() -> TemplateModelProtocol? { model }
|
||||||
model?.rootMolecules ?? []
|
|
||||||
}
|
open func getRootMolecules() -> [MoleculeModelProtocol] { model?.rootMolecules ?? [] }
|
||||||
|
|
||||||
// Needed otherwise when subclassed, the extension gets called.
|
// Needed otherwise when subclassed, the extension gets called.
|
||||||
open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { }
|
open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { }
|
||||||
|
|
||||||
|
public func replaceMoleculeData(_ moleculeModels: [MoleculeModelProtocol]) {
|
||||||
|
pageUpdateQueue.addOperation {
|
||||||
|
let replacedModels:[MoleculeModelProtocol] = moleculeModels.compactMap { model in
|
||||||
|
guard self.attemptToReplace(with: model) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
if replacedModels.count > 0 {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
self.updateUI(for: replacedModels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open func attemptToReplace(with replacementModel: MoleculeModelProtocol) -> Bool {
|
||||||
|
guard var templateModel = getTemplateModel() else { return false }
|
||||||
|
var didReplace = false
|
||||||
|
do {
|
||||||
|
didReplace = try templateModel.replaceMolecule(with: replacementModel)
|
||||||
|
if !didReplace {
|
||||||
|
MVMCoreLoggingHandler.shared()?.addError(toLog: MVMCoreErrorObject(title: nil, messageToLog: "Failed to find '\(replacementModel.id)' in the current screen.", code: ErrorCode.viewControllerProcessingJSON.rawValue, domain: ErrorDomainSystem, location: String(describing: type(of: self)))!)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: String(describing: type(of: self)))!
|
||||||
|
if let error = error as? HumanReadableDecodingErrorProtocol {
|
||||||
|
coreError.messageToLog = "Error replacing molecule \"\(replacementModel.id)\": \(error.readableDescription)"
|
||||||
|
}
|
||||||
|
MVMCoreLoggingHandler.shared()?.addError(toLog: coreError)
|
||||||
|
}
|
||||||
|
return didReplace
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - MVMCoreUIDetailViewProtocol
|
// MARK: - MVMCoreUIDetailViewProtocol
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -76,7 +76,7 @@ public class AddRemoveMoleculesBehavior: PageCustomActionHandlerBehavior, PageMo
|
|||||||
self.delegate = delegateObject
|
self.delegate = delegateObject
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {
|
public func willRender(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
guard let list = delegate?.moleculeListDelegate else { return }
|
guard let list = delegate?.moleculeListDelegate else { return }
|
||||||
for case let model as (MoleculeModelProtocol & ListItemModelProtocol & AddMolecules) in rootMolecules {
|
for case let model as (MoleculeModelProtocol & ListItemModelProtocol & AddMolecules) in rootMolecules {
|
||||||
if let moleculesToAdd = model.getRecursiveMoleculesToAdd(),
|
if let moleculesToAdd = model.getRecursiveMoleculesToAdd(),
|
||||||
|
|||||||
@ -43,7 +43,7 @@ public class PageGetContactBehavior: PageVisibilityBehavior {
|
|||||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
||||||
// TODO: move to protocol function instead
|
// TODO: move to protocol function instead
|
||||||
guard let controller = self?.delegate?.moleculeDelegate as? ViewController else { return }
|
guard let controller = self?.delegate?.moleculeDelegate as? ViewController else { return }
|
||||||
controller.handleNewDataAndUpdateUI()
|
controller.handleNewData()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,11 +45,12 @@ public class GetNotificationAuthStatusBehavior: PageVisibilityBehavior {
|
|||||||
for consumer in consumers {
|
for consumer in consumers {
|
||||||
consumer.consume(notificationStatus: settings.authorizationStatus)
|
consumer.consume(notificationStatus: settings.authorizationStatus)
|
||||||
}
|
}
|
||||||
// Tell template to update
|
|
||||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
Task {
|
||||||
|
// Tell template to update
|
||||||
guard let controller = self.delegate?.moleculeDelegate as? ViewController else { return }
|
guard let controller = self.delegate?.moleculeDelegate as? ViewController else { return }
|
||||||
controller.handleNewDataAndUpdateUI()
|
await controller.handleNewData()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
104
MVMCoreUI/Behaviors/PollingBehaviorModel.swift
Normal file
104
MVMCoreUI/Behaviors/PollingBehaviorModel.swift
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
//
|
||||||
|
// RefreshableMoleculeBehavior.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Kyle Hedden on 9/12/23.
|
||||||
|
// Copyright © 2023 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MVMCore
|
||||||
|
|
||||||
|
public class PollingBehaviorModel: PageBehaviorModelProtocol {
|
||||||
|
public class var identifier: String { "pollingBehavior" }
|
||||||
|
public var shouldAllowMultipleInstances: Bool { true }
|
||||||
|
|
||||||
|
public let refreshInterval: TimeInterval
|
||||||
|
public let refreshAction: ActionModelProtocol
|
||||||
|
|
||||||
|
public var runWhileHidden: Bool
|
||||||
|
public var refreshOnFirstLoad: Bool
|
||||||
|
public var refreshOnShown: Bool
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case refreshInterval
|
||||||
|
case refreshAction
|
||||||
|
case runWhileHidden
|
||||||
|
case refreshOnFirstLoad
|
||||||
|
case refreshOnShown
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init(from decoder: Decoder) throws {
|
||||||
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
refreshInterval = try typeContainer.decode(TimeInterval.self, forKey: .refreshInterval)
|
||||||
|
refreshAction = try typeContainer.decodeModel(codingKey: .refreshAction)
|
||||||
|
runWhileHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .runWhileHidden) ?? false
|
||||||
|
refreshOnFirstLoad = try typeContainer.decodeIfPresent(Bool.self, forKey: .refreshOnFirstLoad) ?? false
|
||||||
|
refreshOnShown = try typeContainer.decodeIfPresent(Bool.self, forKey: .refreshOnShown) ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
try container.encode(refreshInterval, forKey: .refreshInterval)
|
||||||
|
try container.encode(refreshAction, forKey: .refreshAction)
|
||||||
|
try container.encode(runWhileHidden, forKey: .runWhileHidden)
|
||||||
|
try container.encode(refreshOnFirstLoad, forKey: .refreshOnFirstLoad)
|
||||||
|
try container.encode(refreshOnShown, forKey: .refreshOnShown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PollingBehavior: NSObject, PageVisibilityBehavior {
|
||||||
|
|
||||||
|
var model: PollingBehaviorModel
|
||||||
|
var delegateObject: MVMCoreUIDelegateObject?
|
||||||
|
var lastRefresh = Date.distantPast
|
||||||
|
var pollTimer: DispatchSourceTimer?
|
||||||
|
|
||||||
|
var remainingTimeToRefresh: TimeInterval {
|
||||||
|
lastRefresh.timeIntervalSinceNow - model.refreshInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstTimeLoad = true
|
||||||
|
|
||||||
|
var refreshOnShown: Bool {
|
||||||
|
if model.refreshOnFirstLoad && firstTimeLoad {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
firstTimeLoad = false
|
||||||
|
return model.refreshOnShown
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
self.model = model as! PollingBehaviorModel
|
||||||
|
self.delegateObject = delegateObject
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
resumePollingTimer(withRemainingTime: refreshOnShown ? 0 : remainingTimeToRefresh, refreshAction: model.refreshAction, interval: model.refreshInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onPageHidden(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
pollTimer?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func resumePollingTimer(withRemainingTime timeRemaining: TimeInterval, refreshAction: ActionModelProtocol, interval: TimeInterval) {
|
||||||
|
let delegateObject = delegateObject
|
||||||
|
pollTimer?.cancel()
|
||||||
|
pollTimer = DispatchSource.makeTimerSource()
|
||||||
|
pollTimer?.schedule(deadline: .now() + timeRemaining, repeating: interval)
|
||||||
|
pollTimer?.setEventHandler(qos:.utility) {
|
||||||
|
Task {
|
||||||
|
if let delegateActionHandler = delegateObject?.actionDelegate as? ActionDelegateProtocol {
|
||||||
|
try? await delegateActionHandler.performAction(with: refreshAction, additionalData: nil, delegateObject: delegateObject)
|
||||||
|
} else {
|
||||||
|
try? await MVMCoreActionHandler.shared()?.handleAction(with: refreshAction, additionalData: nil, delegateObject: delegateObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pollTimer?.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
pollTimer?.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,16 +1,17 @@
|
|||||||
//
|
//
|
||||||
// PageBehaviorHandlerModelProtocol.swift
|
// PageBehaviorConatinerModelProtocol.swift
|
||||||
// MVMCoreUI
|
// MVMCoreUI
|
||||||
//
|
//
|
||||||
// Created by Scott Pfeil on 3/29/21.
|
// Created by Scott Pfeil on 3/29/21.
|
||||||
// Copyright © 2021 Verizon Wireless. All rights reserved.
|
// Copyright © 2021 Verizon Wireless. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
public protocol PageBehaviorHandlerModelProtocol {
|
/// Protocol applied to a model that contains a list of behavior models.
|
||||||
|
public protocol PageBehaviorContainerModelProtocol {
|
||||||
var behaviors: [PageBehaviorModelProtocol]? { get set }
|
var behaviors: [PageBehaviorModelProtocol]? { get set }
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension PageBehaviorHandlerModelProtocol {
|
public extension PageBehaviorContainerModelProtocol {
|
||||||
|
|
||||||
/// Adds the behavior model to the behaviors if possible.
|
/// Adds the behavior model to the behaviors if possible.
|
||||||
mutating func add(behavior: PageBehaviorModelProtocol) {
|
mutating func add(behavior: PageBehaviorModelProtocol) {
|
||||||
@ -24,7 +25,7 @@ public extension PageBehaviorHandlerModelProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension PageBehaviorHandlerModelProtocol where Self: MoleculeTreeTraversalProtocol {
|
public extension PageBehaviorContainerModelProtocol where Self: MoleculeTreeTraversalProtocol {
|
||||||
|
|
||||||
/// Traverses all models and adds any required behavior models.
|
/// Traverses all models and adds any required behavior models.
|
||||||
mutating func traverseAndAddRequiredBehaviors() {
|
mutating func traverseAndAddRequiredBehaviors() {
|
||||||
@ -12,20 +12,10 @@ public protocol PageBehaviorHandlerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public extension PageBehaviorHandlerProtocol {
|
public extension PageBehaviorHandlerProtocol {
|
||||||
|
|
||||||
/// Creates the behaviors and sets the variable.
|
/// Creates the behaviors and sets the variable.
|
||||||
mutating func createBehaviors(for model: PageBehaviorHandlerModelProtocol, delegateObject: MVMCoreUIDelegateObject?) {
|
func createBehaviors(for behaviorModels: [PageBehaviorModelProtocol], delegateObject: MVMCoreUIDelegateObject?) -> [PageBehaviorProtocol] {
|
||||||
|
var behaviors = [PageBehaviorProtocol]()
|
||||||
behaviors = behaviors?.filter { $0.transcendsPageUpdates }
|
|
||||||
if behaviors?.isEmpty ?? false {
|
|
||||||
behaviors = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let behaviorModels = model.behaviors else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var behaviors: [PageBehaviorProtocol] = behaviors ?? []
|
|
||||||
|
|
||||||
for behaviorModel in behaviorModels {
|
for behaviorModel in behaviorModels {
|
||||||
do {
|
do {
|
||||||
let handlerType = try ModelRegistry.getHandler(behaviorModel) as! PageBehaviorProtocol.Type
|
let handlerType = try ModelRegistry.getHandler(behaviorModel) as! PageBehaviorProtocol.Type
|
||||||
@ -37,7 +27,23 @@ public extension PageBehaviorHandlerProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return behaviors
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func applyBehaviors(pageBehaviorModel: PageBehaviorContainerModelProtocol, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
// Pull the existing behaviors.
|
||||||
|
var behaviors = (behaviors ?? []).filter { $0.transcendsPageUpdates }
|
||||||
|
// Create and append any new behaviors based on the incoming models.
|
||||||
|
let newBehaviors = createBehaviors(for: pageBehaviorModel.behaviors ?? [], delegateObject: delegateObject)
|
||||||
|
behaviors.append(contentsOf: newBehaviors)
|
||||||
|
|
||||||
|
// Apply them to the page.
|
||||||
self.behaviors = behaviors.count > 0 ? behaviors : nil
|
self.behaviors = behaviors.count > 0 ? behaviors : nil
|
||||||
|
|
||||||
|
// Ask the session to apply any more. (Curently inverted contol due to Swift <--> Obj-C conflict.
|
||||||
|
if let viewController = self as? UIViewController {
|
||||||
|
MVMCoreUISession.sharedGlobal()?.applyGlobalBehaviors(to: viewController)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes all behaviors of type.
|
/// Executes all behaviors of type.
|
||||||
@ -49,3 +55,11 @@ public extension PageBehaviorHandlerProtocol {
|
|||||||
return try behaviors?.compactMap({$0 as? T}).allSatisfy({ return try behaviourBlock($0) }) ?? true
|
return try behaviors?.compactMap({$0 as? T}).allSatisfy({ return try behaviourBlock($0) }) ?? true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension PageBehaviorHandlerProtocol where Self: MVMCoreViewControllerProtocol {
|
||||||
|
|
||||||
|
mutating func applyBehaviors(pageBehaviorModel: PageBehaviorContainerModelProtocol) {
|
||||||
|
applyBehaviors(pageBehaviorModel: pageBehaviorModel, delegateObject: delegateObject?() as? MVMCoreUIDelegateObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -14,12 +14,15 @@ public protocol PageBehaviorProtocol: ModelHandlerProtocol {
|
|||||||
/// Should the behavior persist regardless of page behavior model updates.
|
/// Should the behavior persist regardless of page behavior model updates.
|
||||||
var transcendsPageUpdates: Bool { get }
|
var transcendsPageUpdates: Bool { get }
|
||||||
|
|
||||||
|
func modulesToListenFor() -> [String]
|
||||||
|
|
||||||
/// Initializes the behavior with the model
|
/// Initializes the behavior with the model
|
||||||
init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?)
|
init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension PageBehaviorProtocol {
|
public extension PageBehaviorProtocol {
|
||||||
var transcendsPageUpdates: Bool { return false }
|
var transcendsPageUpdates: Bool { return false }
|
||||||
|
func modulesToListenFor() -> [String] { return [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,6 +35,7 @@ public protocol PageMoleculeTransformationBehavior: PageBehaviorProtocol {
|
|||||||
func didSetupMolecule(view: MoleculeViewProtocol, withModel: MoleculeModelProtocol)
|
func didSetupMolecule(view: MoleculeViewProtocol, withModel: MoleculeModelProtocol)
|
||||||
func willSetupNavigationBar(with model: NavigationItemModelProtocol, updating view: UINavigationBar)
|
func willSetupNavigationBar(with model: NavigationItemModelProtocol, updating view: UINavigationBar)
|
||||||
func didSetupNavigationBar(view: UINavigationBar, with model: NavigationItemModelProtocol)
|
func didSetupNavigationBar(view: UINavigationBar, with model: NavigationItemModelProtocol)
|
||||||
|
func willRender(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?)
|
||||||
func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject) throws -> Bool
|
func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject) throws -> Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,11 +46,11 @@ public extension PageMoleculeTransformationBehavior {
|
|||||||
func didSetupMolecule(view: MoleculeViewProtocol, withModel: MoleculeModelProtocol) {}
|
func didSetupMolecule(view: MoleculeViewProtocol, withModel: MoleculeModelProtocol) {}
|
||||||
func willSetupNavigationBar(with model: NavigationItemModelProtocol, updating view: UINavigationBar) {}
|
func willSetupNavigationBar(with model: NavigationItemModelProtocol, updating view: UINavigationBar) {}
|
||||||
func didSetupNavigationBar(view: UINavigationBar, with model: NavigationItemModelProtocol) {}
|
func didSetupNavigationBar(view: UINavigationBar, with model: NavigationItemModelProtocol) {}
|
||||||
|
func willRender(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {}
|
||||||
func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject) throws -> Bool { return true }
|
func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject) throws -> Bool { return true }
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol PageVisibilityBehavior: PageBehaviorProtocol {
|
public protocol PageVisibilityBehavior: PageBehaviorProtocol {
|
||||||
|
|
||||||
func willShowPage(_ delegateObject: MVMCoreUIDelegateObject?)
|
func willShowPage(_ delegateObject: MVMCoreUIDelegateObject?)
|
||||||
func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?)
|
func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?)
|
||||||
func willHidePage(_ delegateObject: MVMCoreUIDelegateObject?)
|
func willHidePage(_ delegateObject: MVMCoreUIDelegateObject?)
|
||||||
@ -84,8 +88,8 @@ public protocol PageCustomActionHandlerBehavior: PageBehaviorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public extension MVMCoreUIDelegateObject {
|
public extension MVMCoreUIDelegateObject {
|
||||||
var behaviorModelDelegate: PageBehaviorHandlerModelProtocol? {
|
var behaviorModelDelegate: PageBehaviorContainerModelProtocol? {
|
||||||
(moleculeDelegate as? PageProtocol)?.pageModel as? PageBehaviorHandlerModelProtocol
|
(moleculeDelegate as? PageProtocol)?.pageModel as? PageBehaviorContainerModelProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
weak var behaviorTemplateDelegate: (PageBehaviorHandlerProtocol & NSObjectProtocol)? {
|
weak var behaviorTemplateDelegate: (PageBehaviorHandlerProtocol & NSObjectProtocol)? {
|
||||||
|
|||||||
110
MVMCoreUI/Behaviors/ReplaceableMoleculeBehaviorModel.swift
Normal file
110
MVMCoreUI/Behaviors/ReplaceableMoleculeBehaviorModel.swift
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
//
|
||||||
|
// ReplacementMoleculeBehavior.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Kyle Hedden on 9/12/23.
|
||||||
|
// Copyright © 2023 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MVMCore
|
||||||
|
|
||||||
|
public class ReplaceableMoleculeBehaviorModel: PageBehaviorModelProtocol {
|
||||||
|
public class var identifier: String { "replaceMoleculeBehavior" }
|
||||||
|
public var shouldAllowMultipleInstances: Bool { true }
|
||||||
|
public var moleculeIds: [String]
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior {
|
||||||
|
var moleculeIds: [String]
|
||||||
|
var modulesToListenFor: [String]
|
||||||
|
private var observingForResponses: NSObjectProtocol?
|
||||||
|
private var delegateObject: MVMCoreUIDelegateObject?
|
||||||
|
|
||||||
|
public var transcendsPageUpdates: Bool { true }
|
||||||
|
|
||||||
|
public required init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
moleculeIds = (model as! ReplaceableMoleculeBehaviorModel).moleculeIds
|
||||||
|
let shouldListenForListUpdates = delegateObject?.moleculeListDelegate != nil
|
||||||
|
if shouldListenForListUpdates {
|
||||||
|
modulesToListenFor = []
|
||||||
|
listenForModuleUpdates()
|
||||||
|
} else {
|
||||||
|
modulesToListenFor = moleculeIds
|
||||||
|
stopListeningForModuleUpdates()
|
||||||
|
}
|
||||||
|
self.delegateObject = delegateObject
|
||||||
|
guard let pageType = delegateObject?.moleculeDelegate?.getTemplateModel()?.pageType else { return }
|
||||||
|
MVMCoreViewControllerMappingObject.shared()?.addOptionalModules(toMapping: moleculeIds, forPageType: pageType)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
self.delegateObject = delegateObject
|
||||||
|
let shouldListenForListUpdates = delegateObject?.moleculeListDelegate != nil
|
||||||
|
if shouldListenForListUpdates {
|
||||||
|
modulesToListenFor = []
|
||||||
|
listenForModuleUpdates()
|
||||||
|
} else {
|
||||||
|
modulesToListenFor = moleculeIds
|
||||||
|
stopListeningForModuleUpdates()
|
||||||
|
}
|
||||||
|
|
||||||
|
let moleculeModels = moleculeIds.compactMap { moleculeId in
|
||||||
|
do {
|
||||||
|
return try delegateObject?.moleculeDelegate?.getModuleWithName(moleculeId)
|
||||||
|
} catch {
|
||||||
|
let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: String(describing: type(of: self)))!
|
||||||
|
if let error = error as? HumanReadableDecodingErrorProtocol {
|
||||||
|
coreError.messageToLog = "Error decoding replacement \"\(moleculeId)\": \(error.readableDescription)"
|
||||||
|
}
|
||||||
|
MVMCoreLoggingHandler.shared()?.addError(toLog: coreError)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if moleculeModels.count > 0 {
|
||||||
|
delegateObject?.moleculeDelegate?.replaceMoleculeData(moleculeModels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func listenForModuleUpdates() {
|
||||||
|
guard observingForResponses == nil else { return }
|
||||||
|
let pageUpdateQueue = OperationQueue()
|
||||||
|
pageUpdateQueue.maxConcurrentOperationCount = 1
|
||||||
|
pageUpdateQueue.qualityOfService = .userInteractive
|
||||||
|
observingForResponses = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil, queue: pageUpdateQueue, using: responseJSONUpdated(notification:))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopListeningForModuleUpdates() {
|
||||||
|
guard let observingForResponses = observingForResponses else { return }
|
||||||
|
NotificationCenter.default.removeObserver(observingForResponses)
|
||||||
|
self.observingForResponses = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func responseJSONUpdated(notification: Notification) {
|
||||||
|
guard let modulesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyModuleMap) else { return }
|
||||||
|
let modules: [MoleculeModelProtocol] = moleculeIds.compactMap { moleculeId in
|
||||||
|
guard let json = modulesLoaded.optionalDictionaryForKey(moleculeId) else { return nil }
|
||||||
|
do {
|
||||||
|
return try convertToModel(moduleJSON: json)
|
||||||
|
} catch {
|
||||||
|
let coreError = MVMCoreErrorObject.createErrorObject(for: error, location: String(describing: type(of: self)))!
|
||||||
|
if let error = error as? HumanReadableDecodingErrorProtocol {
|
||||||
|
coreError.messageToLog = "Error decoding replacement \"\(moleculeId)\": \(error.readableDescription)"
|
||||||
|
}
|
||||||
|
MVMCoreLoggingHandler.shared()?.addError(toLog: coreError)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if modules.count > 0 {
|
||||||
|
delegateObject?.moleculeDelegate?.replaceMoleculeData(modules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func convertToModel(moduleJSON: [String: Any]) throws -> MoleculeModelProtocol {
|
||||||
|
guard let moleculeName = moduleJSON.optionalStringForKey(KeyMoleculeName),
|
||||||
|
let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) else {
|
||||||
|
throw ModelRegistry.Error.decoderErrorModelNotMapped(identifer: moduleJSON.optionalStringForKey(KeyMoleculeName))
|
||||||
|
}
|
||||||
|
return try modelType.decode(jsonDict: moduleJSON as [String : Any]) as! MoleculeModelProtocol
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -231,6 +231,8 @@ open class CoreUIModelMapping: ModelMapping {
|
|||||||
ModelRegistry.register(handler: PageGetContactBehavior.self, for: PageGetContactBehaviorModel.self)
|
ModelRegistry.register(handler: PageGetContactBehavior.self, for: PageGetContactBehaviorModel.self)
|
||||||
ModelRegistry.register(handler: AddRemoveMoleculesBehavior.self, for: AddRemoveMoleculesBehaviorModel.self)
|
ModelRegistry.register(handler: AddRemoveMoleculesBehavior.self, for: AddRemoveMoleculesBehaviorModel.self)
|
||||||
ModelRegistry.register(handler: GetNotificationAuthStatusBehavior.self, for: GetNotificationAuthStatusBehaviorModel.self)
|
ModelRegistry.register(handler: GetNotificationAuthStatusBehavior.self, for: GetNotificationAuthStatusBehaviorModel.self)
|
||||||
|
ModelRegistry.register(handler: ReplaceableMoleculeBehavior.self, for: ReplaceableMoleculeBehaviorModel.self)
|
||||||
|
ModelRegistry.register(handler: PollingBehavior.self, for: PollingBehaviorModel.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
open override class func registerActions() {
|
open override class func registerActions() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user