diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d475e062..8dc2e960 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,9 +22,9 @@ download_artifacts: - bash_shell environment: name: oneartifactory - url: https://oneartifactoryprod.verizon.com/artifactory + url: https://oneartifactoryci.verizon.com/artifactory variables: - ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory + ARTIFACTORY_URL: https://oneartifactoryci.verizon.com/artifactory build_project: stage: build @@ -47,9 +47,9 @@ deploy_snapshot: - bash_shell environment: name: oneartifactory - url: https://oneartifactoryprod.verizon.com/artifactory + url: https://oneartifactoryci.verizon.com/artifactory variables: - ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory + ARTIFACTORY_URL: https://oneartifactoryci.verizon.com/artifactory #promote_snapshot: # stage: go live @@ -65,9 +65,9 @@ deploy_snapshot: # - bash_shell # environment: # name: oneartifactory -# url: https://oneartifactoryprod.verizon.com/artifactory +# url: https://oneartifactoryci.verizon.com/artifactory # variables: -# ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory +# ARTIFACTORY_URL: https://oneartifactoryci.verizon.com/artifactory # #create_version_tag: # stage: tag diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 7fa7844c..d394d55d 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -597,9 +597,18 @@ EA5124FF2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */; }; EA7E67742758310500ABF773 /* EnableFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7E67732758310500ABF773 /* EnableFormFieldEffectModel.swift */; }; EA7E67762758365300ABF773 /* UIUpdatableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7E67752758365300ABF773 /* UIUpdatableModelProtocol.swift */; }; + EA985C3E2970938F00F2FF2E /* Tilelet.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C3D2970938F00F2FF2E /* Tilelet.swift */; }; + EA985C402970939A00F2FF2E /* TileletModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C3F2970939A00F2FF2E /* TileletModel.swift */; }; + EA985C602970A3F000F2FF2E /* VDS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA985C5F2970A3F000F2FF2E /* VDS.framework */; }; + EA985C642970A40E00F2FF2E /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA985C632970A40E00F2FF2E /* VDSTypographyTokens.xcframework */; }; + EA985C852981AA9C00F2FF2E /* VDS-Enums+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C842981AA9C00F2FF2E /* VDS-Enums+Codable.swift */; }; + EA985C872981AB0F00F2FF2E /* VDS-Tilelet+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C862981AB0F00F2FF2E /* VDS-Tilelet+Codable.swift */; }; + EA985C892981AB7100F2FF2E /* VDS-TextStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C882981AB7100F2FF2E /* VDS-TextStyle.swift */; }; + EA985C8B2983259900F2FF2E /* VDS-LabelAttributeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C8A2983259900F2FF2E /* VDS-LabelAttributeModel.swift */; }; EAA0CFAF275E7D8000D65EB0 /* FormFieldEffectProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFAE275E7D8000D65EB0 /* FormFieldEffectProtocol.swift */; }; EAA0CFB1275E823A00D65EB0 /* HideFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFB0275E823A00D65EB0 /* HideFormFieldEffectModel.swift */; }; EAA0CFB3275E831E00D65EB0 /* DisableFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFB2275E831E00D65EB0 /* DisableFormFieldEffectModel.swift */; }; + EAA78020290081320057DFDF /* VDSMoleculeViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA7801F290081320057DFDF /* VDSMoleculeViewProtocol.swift */; }; EAB14BC127D935F00012AB2C /* RuleCompareModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB14BC027D935F00012AB2C /* RuleCompareModelProtocol.swift */; }; EAB14BC327D9378D0012AB2C /* RuleAnyModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */; }; EABFC1412763BB8D00E78B40 /* FormLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC1402763BB8D00E78B40 /* FormLabel.swift */; }; @@ -1201,9 +1210,18 @@ EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButtonModel.swift; sourceTree = ""; }; EA7E67732758310500ABF773 /* EnableFormFieldEffectModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnableFormFieldEffectModel.swift; sourceTree = ""; }; EA7E67752758365300ABF773 /* UIUpdatableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIUpdatableModelProtocol.swift; sourceTree = ""; }; + EA985C3D2970938F00F2FF2E /* Tilelet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tilelet.swift; sourceTree = ""; }; + EA985C3F2970939A00F2FF2E /* TileletModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TileletModel.swift; sourceTree = ""; }; + EA985C5F2970A3F000F2FF2E /* VDS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = VDS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EA985C632970A40E00F2FF2E /* VDSTypographyTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTypographyTokens.xcframework; path = ../SharedFrameworks/VDSTypographyTokens.xcframework; sourceTree = ""; }; + EA985C842981AA9C00F2FF2E /* VDS-Enums+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VDS-Enums+Codable.swift"; sourceTree = ""; }; + EA985C862981AB0F00F2FF2E /* VDS-Tilelet+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VDS-Tilelet+Codable.swift"; sourceTree = ""; }; + EA985C882981AB7100F2FF2E /* VDS-TextStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VDS-TextStyle.swift"; sourceTree = ""; }; + EA985C8A2983259900F2FF2E /* VDS-LabelAttributeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VDS-LabelAttributeModel.swift"; sourceTree = ""; }; EAA0CFAE275E7D8000D65EB0 /* FormFieldEffectProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormFieldEffectProtocol.swift; sourceTree = ""; }; EAA0CFB0275E823A00D65EB0 /* HideFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideFormFieldEffectModel.swift; sourceTree = ""; }; EAA0CFB2275E831E00D65EB0 /* DisableFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableFormFieldEffectModel.swift; sourceTree = ""; }; + EAA7801F290081320057DFDF /* VDSMoleculeViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDSMoleculeViewProtocol.swift; sourceTree = ""; }; EAB14BC027D935F00012AB2C /* RuleCompareModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleCompareModelProtocol.swift; sourceTree = ""; }; EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyModelProtocol.swift; sourceTree = ""; }; EABFC1402763BB8D00E78B40 /* FormLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabel.swift; sourceTree = ""; }; @@ -1218,7 +1236,9 @@ files = ( D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */, AFE4A1D127DFB5EE00C458D0 /* VDSColorTokens.xcframework in Frameworks */, + EA985C602970A3F000F2FF2E /* VDS.framework in Frameworks */, 187FEB2A2844D2A600BF29C2 /* VDSFormControlsTokens.xcframework in Frameworks */, + EA985C642970A40E00F2FF2E /* VDSTypographyTokens.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1611,6 +1631,10 @@ D2ED27E7254B0CE600A1C293 /* UIAlertControllerStyle+Extension.swift */, D2E0FFF726AF68530085D696 /* UITableViewRowAnimation+Extension.swift */, 4457904D27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift */, + EA985C842981AA9C00F2FF2E /* VDS-Enums+Codable.swift */, + EA985C862981AB0F00F2FF2E /* VDS-Tilelet+Codable.swift */, + EA985C882981AB7100F2FF2E /* VDS-TextStyle.swift */, + EA985C8A2983259900F2FF2E /* VDS-LabelAttributeModel.swift */, ); path = Extensions; sourceTree = ""; @@ -2064,6 +2088,8 @@ D29DF0E421E4F3C7003B2FB9 /* Frameworks */ = { isa = PBXGroup; children = ( + EA985C632970A40E00F2FF2E /* VDSTypographyTokens.xcframework */, + EA985C5F2970A3F000F2FF2E /* VDS.framework */, 187FEB292844D2A600BF29C2 /* VDSFormControlsTokens.xcframework */, AFE4A1D027DFB5EE00C458D0 /* VDSColorTokens.xcframework */, D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */, @@ -2261,6 +2287,8 @@ AA37CBD42519072F0027344C /* Stars.swift */, AA07EA902510A442009A2AE3 /* StarModel.swift */, AA07EA922510A451009A2AE3 /* Star.swift */, + EA985C3D2970938F00F2FF2E /* Tilelet.swift */, + EA985C3F2970939A00F2FF2E /* TileletModel.swift */, ); path = Views; sourceTree = ""; @@ -2458,6 +2486,7 @@ D2B9D0E3265EEE9D0084735C /* MoleculeListProtocol.swift */, 011B58EE23A2AA850085F53C /* ModelProtocols */, 27559EFB27D691D3000836C1 /* ViewMaskingProtocol.swift */, + EAA7801F290081320057DFDF /* VDSMoleculeViewProtocol.swift */, ); path = Protocols; sourceTree = ""; @@ -2741,6 +2770,7 @@ D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */, EAB14BC127D935F00012AB2C /* RuleCompareModelProtocol.swift in Sources */, 011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */, + EA985C892981AB7100F2FF2E /* VDS-TextStyle.swift in Sources */, BB2BF0EA2452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift in Sources */, D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */, D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */, @@ -2765,6 +2795,7 @@ 012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */, BB6C6AC1242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift in Sources */, 32D2609724C19E2100B56344 /* LockupsPlanSMLXLModel.swift in Sources */, + EA985C872981AB0F00F2FF2E /* VDS-Tilelet+Codable.swift in Sources */, D2092351244F7BE80044AD09 /* ThreeLayerCenterTemplateModel.swift in Sources */, D21B7F77243BB70700051ABF /* MoleculeCollectionItemModel.swift in Sources */, AAB9C10A243496DD00151545 /* RadioSwatch.swift in Sources */, @@ -2783,7 +2814,9 @@ D2E2A99D23DA3217000B42E6 /* UIStackViewAlignment+Extension.swift in Sources */, 01EB369423609801006832FA /* HeadlineBodyModel.swift in Sources */, D2A92884241ACB25004E01C6 /* ProgrammaticScrollViewController.swift in Sources */, + EA985C3E2970938F00F2FF2E /* Tilelet.swift in Sources */, D23A90002612347A007E14CE /* PageBehaviorHandlerModelProtocol.swift in Sources */, + EAA78020290081320057DFDF /* VDSMoleculeViewProtocol.swift in Sources */, 0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */, D2E2A99F23E07F8A000B42E6 /* PillButton.swift in Sources */, D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */, @@ -2968,6 +3001,7 @@ FD99130028E21E4900542CC3 /* RuleNotEqualsModel.swift in Sources */, BBC0C4FD24811DBC0087C44F /* Tag.swift in Sources */, 94C2D9842386F3F80006CF46 /* LabelAttributeModel.swift in Sources */, + EA985C8B2983259900F2FF2E /* VDS-LabelAttributeModel.swift in Sources */, 944589212385D6E900DE9FD4 /* DashLineModel.swift in Sources */, D2E2A99623D8CF85000B42E6 /* HeadlineBodyLinkToggleModel.swift in Sources */, C6FA7D5323C77A4A00A3614A /* StringAndMoleculeStack.swift in Sources */, @@ -3092,6 +3126,7 @@ BB2FB3BB247E7EBC00DF73CD /* TagCollectionViewCell.swift in Sources */, 012A88C6238DA34000FE3DA1 /* ModuleMoleculeModel.swift in Sources */, 94C2D9A123872BCC0006CF46 /* LabelAttributeUnderlineModel.swift in Sources */, + EA985C852981AA9C00F2FF2E /* VDS-Enums+Codable.swift in Sources */, AAB8549824DC01BD00477C40 /* ListThreeColumnBillHistoryDividerModel.swift in Sources */, D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */, D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */, @@ -3178,6 +3213,7 @@ D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */, 012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */, 27F6B08C26052AFF008529AA /* ParentMoleculeModelProtocol.swift in Sources */, + EA985C402970939A00F2FF2E /* TileletModel.swift in Sources */, 0AB764D324460FA400E7FE72 /* UIPickerView+Extension.swift in Sources */, D29DF29E21E7AE3B003B2FB9 /* MFStyler.m in Sources */, 94C661D923CCF4B400D9FE5B /* LeftRightLabelModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift index 9efc554a..f771dddf 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/Checkbox.swift @@ -314,7 +314,7 @@ import MVMCore } /// Adjust accessibility label based on state of Checkbox. - func updateAccessibilityLabel() { + public func updateAccessibilityLabel() { // Attention: This needs to be addressed with the accessibility team. // NOTE: Currently emptying description part of MVMCoreUICheckBox accessibility label to avoid crashing! if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") { diff --git a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift index 858984b0..58f398b6 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CheckboxLabelModel.swift @@ -15,8 +15,8 @@ public enum CheckboxPosition: String, Codable { case bottom } -@objcMembers public class CheckboxLabelModel: MoleculeModelProtocol { - public static var identifier: String = "checkboxLabel" +@objcMembers open class CheckboxLabelModel: MoleculeModelProtocol { + open class var identifier: String { "checkboxLabel" } public var moleculeName: String = CheckboxLabelModel.identifier public var backgroundColor: Color? public var checkboxAlignment: CheckboxPosition? diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 059c5637..142bc700 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -741,36 +741,6 @@ public typealias ActionBlock = () -> () clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1)) } - /** - Provides a text container and layout manager of how the text would appear on screen. - They are used in tandem to derive low-level TextKit results of the label. - */ - public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager, NSTextStorage)? { - - // Must configure the attributed string to translate what would appear on screen to accurately analyze. - guard let attributedText = attributedText else { return nil } - - let paragraph = NSMutableParagraphStyle() - paragraph.alignment = textAlignment - - let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText) - stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count)) - - let textStorage = NSTextStorage(attributedString: stagedAttributedString) - let layoutManager = NSLayoutManager() - let textContainer = NSTextContainer(size: .zero) - - layoutManager.addTextContainer(textContainer) - textStorage.addLayoutManager(layoutManager) - - textContainer.lineFragmentPadding = 0.0 - textContainer.lineBreakMode = lineBreakMode - textContainer.maximumNumberOfLines = numberOfLines - textContainer.size = bounds.size - - return (textContainer, layoutManager, textStorage) - } - public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { guard let abstractContainer = label.abstractTextContainer() else { return CGRect() } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift index d6ec1b74..03d0ff08 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelAttributeImageModel.swift @@ -17,6 +17,7 @@ class LabelAttributeImageModel: LabelAttributeModel { var size: CGFloat? var name: String? var URL: String? + var tintColor: Color? //-------------------------------------------------- // MARK: - Initializer @@ -34,6 +35,7 @@ class LabelAttributeImageModel: LabelAttributeModel { case size case name case URL + case tintColor } //-------------------------------------------------- @@ -55,6 +57,7 @@ class LabelAttributeImageModel: LabelAttributeModel { size = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .size) name = try typeContainer.decodeIfPresent(String.self, forKey: .name) URL = try typeContainer.decodeIfPresent(String.self, forKey: .URL) + tintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .tintColor) try super.init(from: decoder) } @@ -64,5 +67,6 @@ class LabelAttributeImageModel: LabelAttributeModel { try container.encodeIfPresent(size, forKey: .size) try container.encodeIfPresent(name, forKey: .name) try container.encodeIfPresent(URL, forKey: .URL) + try container.encodeIfPresent(tintColor, forKey: .tintColor) } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift index 10e6a4e5..d1995d19 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift @@ -7,12 +7,13 @@ // -@objcMembers open class LabelModel: MoleculeModelProtocol { +@objcMembers open class LabelModel: MoleculeModelProtocol, Identifiable { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- open class var identifier: String { "label" } + public var id: String public var backgroundColor: Color? public var text: String public var accessibilityText: String? @@ -34,6 +35,7 @@ private enum CodingKeys: String, CodingKey { case moleculeName + case id case text case accessibilityText case textColor @@ -58,7 +60,8 @@ // MARK: - Initializer //-------------------------------------------------- - public init(text: String) { + public init(id: String = UUID().uuidString, text: String) { + self.id = id self.text = text } @@ -78,6 +81,7 @@ required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString text = try typeContainer.decode(String.self, forKey: .text) accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) textColor = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor) @@ -102,6 +106,7 @@ open func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(id, forKey: .id) try container.encode(text, forKey: .text) try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) try container.encodeIfPresent(textColor, forKey: .textColor) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Tilelet.swift b/MVMCoreUI/Atomic/Atoms/Views/Tilelet.swift new file mode 100644 index 00000000..c87e091c --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/Tilelet.swift @@ -0,0 +1,85 @@ +// +// Tilet.swift +// MVMCoreUI +// +// Created by Matt Bruce on 1/12/23. +// Copyright © 2023 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore +import VDS +import Combine + +/** + This class expects its height and width to be equal. + */ +open class Tilelet: VDS.Tilelet, VDSMoleculeViewProtocol{ + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public var viewModel: TileletModel! + public var delegateObject: MVMCoreUIDelegateObject? + public var additionalData: [AnyHashable: Any]? + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public convenience required init() { + self.init(frame: .zero) + } + + //-------------------------------------------------- + // MARK: - Public + //-------------------------------------------------- + public func viewModelDidUpdate() { + color = viewModel.color + padding = viewModel.padding + aspectRatio = viewModel.aspectRatio + width = viewModel.width + textWidth = viewModel.textWidth + textPercentage = viewModel.textPercentage + titleModel = viewModel.titleModel(delegateObject: delegateObject, additionalData: additionalData) + subTitleModel = viewModel.subTitleModel(delegateObject: delegateObject, additionalData: additionalData) + badgeModel = viewModel.badge + descriptiveIconModel = viewModel.descriptiveIcon + directionalIconModel = viewModel.directionalIcon + //setup action + if let action = viewModel.action { + //add the subscriber + onClickSubscriber = publisher(for: .touchUpInside) + .sink {[weak self] control in + guard let self else { return } + MVMCoreUIActionHandler.performActionUnstructured(with: action, + sourceModel: self.viewModel, + additionalData: self.additionalData, + delegateObject: self.delegateObject) + } + } + } + + //-------------------------------------------------- + // MARK: - MVMCoreViewProtocol + //-------------------------------------------------- + open func updateView(_ size: CGFloat) {} + + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + //since this is a class func, we can't reference it directly + public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + 100 + } + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + open override func layoutSubviews() { + super.layoutSubviews() + // Accounts for any collection size changes + DispatchQueue.main.async { [weak self] in + guard let self else { return } + self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + } + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift new file mode 100644 index 00000000..c6685364 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift @@ -0,0 +1,103 @@ +// +// TiletModel.swift +// MVMCoreUI +// +// Created by Matt Bruce on 1/12/23. +// Copyright © 2023 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS + +open class TileletModel: MoleculeModelProtocol { + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "tilelet" + public var backgroundColor: Color? + public var color: TileContainer.BackgroundColor + public var padding: TileContainer.Padding + public var aspectRatio: TileContainer.AspectRatio + public var badge: Tilelet.BadgeModel? + public var title: LabelModel? + public var subTitle: LabelModel? + public var descriptiveIcon: Tilelet.DescriptiveIcon? + public var directionalIcon: Tilelet.DirectionalIcon? + public var width: CGFloat? + public var textWidth: CGFloat? + public var textPercentage: CGFloat? + public var action: ActionModelProtocol? + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case color + case padding + case aspectRatio + case badge + case title + case subTitle + case descriptiveIcon + case directionalIcon + case width + case textWidth + case textPercentage + case action + } + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.backgroundColor = try container.decodeIfPresent(Color.self, forKey: .backgroundColor) + self.color = try container.decodeIfPresent(TileContainer.BackgroundColor.self, forKey: .color) ?? TileContainer.BackgroundColor.black + self.padding = try container.decodeIfPresent(TileContainer.Padding.self, forKey: .padding) ?? TileContainer.Padding.padding4X + self.aspectRatio = try container.decodeIfPresent(TileContainer.AspectRatio.self, forKey: .aspectRatio) ?? TileContainer.AspectRatio.none + self.badge = try container.decodeIfPresent(Tilelet.BadgeModel.self, forKey: .badge) + self.title = try container.decodeIfPresent(LabelModel.self, forKey: .title) + self.subTitle = try container.decodeIfPresent(LabelModel.self, forKey: .subTitle) + self.descriptiveIcon = try container.decodeIfPresent(Tilelet.DescriptiveIcon.self, forKey: .descriptiveIcon) + self.directionalIcon = try container.decodeIfPresent(Tilelet.DirectionalIcon.self, forKey: .directionalIcon) + self.width = try container.decodeIfPresent(CGFloat.self, forKey: .width) + self.textWidth = try container.decodeIfPresent(CGFloat.self, forKey: .textWidth) + self.textPercentage = try container.decodeIfPresent(CGFloat.self, forKey: .textPercentage) + action = try container.decodeModelIfPresent(codingKey: .action) + } + + public func titleModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Tilelet.TitleModel? { + guard let title else { return nil } + let attrs = title.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) + let style: Tilelet.TitleModel.TextStyle? = title.fontStyle?.vdsSubsetStyle() + if let style { + return .init(text: title.text, textAttributes: attrs, textStyle: style) + } else { + return .init(text: title.text, textAttributes: attrs) + } + } + + public func subTitleModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Tilelet.SubTitleModel? { + guard let subTitle else { return nil } + let style: Tilelet.SubTitleModel.TextStyle? = subTitle.fontStyle?.vdsSubsetStyle() + if let style { + return .init(text: subTitle.text, textStyle: style) + } else { + return .init(text: subTitle.text) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(color, forKey: .color) + try container.encodeIfPresent(padding, forKey: .padding) + try container.encodeIfPresent(aspectRatio, forKey: .aspectRatio) + try container.encodeIfPresent(badge, forKey: .badge) + try container.encodeIfPresent(title, forKey: .title) + try container.encodeIfPresent(subTitle, forKey: .subTitle) + try container.encodeIfPresent(descriptiveIcon, forKey: .descriptiveIcon) + try container.encodeIfPresent(directionalIcon, forKey: .directionalIcon) + try container.encodeIfPresent(width, forKey: .width) + try container.encodeIfPresent(textWidth, forKey: .textWidth) + try container.encodeIfPresent(textPercentage, forKey: .textPercentage) + try container.encodeModelIfPresent(action, forKey: .action) + } +} diff --git a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift new file mode 100644 index 00000000..6c143d9c --- /dev/null +++ b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift @@ -0,0 +1,18 @@ +// +// VDS-Enums+Codable.swift +// MVMCoreUI +// +// Created by Matt Bruce on 1/25/23. +// Copyright © 2023 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS + +extension Surface: Codable {} +extension Badge.FillColor: Codable {} +extension Icon.Name: Codable {} +extension Icon.Size: Codable {} +extension TileContainer.BackgroundColor: Codable {} +extension TileContainer.Padding: Codable {} +extension TileContainer.AspectRatio: Codable {} diff --git a/MVMCoreUI/Atomic/Extensions/VDS-LabelAttributeModel.swift b/MVMCoreUI/Atomic/Extensions/VDS-LabelAttributeModel.swift new file mode 100644 index 00000000..4eab03b2 --- /dev/null +++ b/MVMCoreUI/Atomic/Extensions/VDS-LabelAttributeModel.swift @@ -0,0 +1,205 @@ +// +// VDS-LabelAttributeModel.swift +// MVMCoreUI +// +// Created by Matt Bruce on 1/26/23. +// Copyright © 2023 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS +import Combine + +//Meant to be used to convert Any Array of type LabelAttributeModel +extension Array where Element: MVMCoreUI.LabelAttributeModel { + public func toVDSLabelAttributeModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> [any VDS.LabelAttributeModel] { + var attributes: [any VDS.LabelAttributeModel] = [] + forEach { atomicLabelAttribute in + if let attr = atomicLabelAttribute as? (any VDSLabelAttributeConvertable), + let vds = attr.convertToVDSLabelAttirbute(delegateObject: delegateObject, + additionalData: additionalData){ + attributes.append(vds) + } + } + return attributes + } +} + +//VDS Convertable Protocol and Extensions +public protocol VDSLabelAttributeConvertable { + associatedtype LabelAttributeType: VDS.LabelAttributeModel + func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> LabelAttributeType? +} + +extension LabelAttributeUnderlineModel: VDSLabelAttributeConvertable { + + public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> UnderlineLabelAttribute? { + guard let style = UnderlineLabelAttribute.Style(rawValue: style.rawValue) else { return nil } + + var pattern: UnderlineLabelAttribute.Pattern? + if let attrPattern = pattern { + pattern = UnderlineLabelAttribute.Pattern(rawValue: attrPattern.rawValue) + } + + return VDS.UnderlineLabelAttribute(location: location, + length: length, + style: style, + color: color?.uiColor, + pattern: pattern) + } +} + +extension LabelAttributeActionModel: VDSLabelAttributeConvertable { + public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> ActionLabelAttribute? { + var vdsAttribute = VDS.ActionLabelAttribute(location: location, length: length) + vdsAttribute.subscriber = vdsAttribute.action.sink { [weak self] in + guard let self else { return } + MVMCoreUIActionHandler.performActionUnstructured(with: self.action, + sourceModel: nil, + additionalData: additionalData, + delegateObject: delegateObject) + } + return vdsAttribute + } +} + +extension LabelAttributeFontModel: VDSLabelAttributeConvertable { + public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> TextStyleLabelAttribute? { + + var textStyle: TextStyle? + if let found = style?.vdsTextStyle() { + textStyle = found + } else if let name, let found = TextStyle(rawValue: name) { + textStyle = found + } + + guard let textStyle else { return nil } + + return TextStyleLabelAttribute(location: location, + length: length, + textStyle: textStyle) + } +} + +extension LabelAttributeColorModel: VDSLabelAttributeConvertable { + public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> ColorLabelAttribute? { + guard let textColor else { return nil } + return ColorLabelAttribute(location: location, + length: length, + color: textColor.uiColor) + } +} + +extension LabelAttributeStrikeThroughModel: VDSLabelAttributeConvertable { + public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> StrikeThroughLabelAttribute? { + return StrikeThroughLabelAttribute(location: location, + length: length) + } +} + +extension LabelAttributeImageModel: VDSLabelAttributeConvertable { + public func convertToVDSLabelAttirbute(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> AtomicImageLabelAttribute? { + var frame: CGRect? + if let size { + frame = CGRect(x: 0, y: 0, width: size, height: size) + } + return AtomicImageLabelAttribute(location: location, + length: length, + imageName: name, + imageURL: URL, + frame: frame, + tintColor: tintColor?.uiColor) + } +} + +//Custom Atomic Additions for the ImageAttachment +public class AtomicImageLabelAttribute: AttachmentLabelAttributeModel { + //-------------------------------------------------- + // MARK: - Enums + //-------------------------------------------------- + public enum Error: Swift.Error { + case bundleNotFound + case imageNotFound(String) + case imageNotSet + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + public var id = UUID() + public var location: Int + public var length: Int = 1 + public var imageName: String? + public var imageURL: String? + public var image: UIImage? + public var frame: CGRect? + public var tintColor: UIColor? + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + public init(location: Int, length: Int = 1, imageName: String? = nil, imageURL: String? = nil, + image: UIImage? = nil, frame: CGRect? = nil, tintColor: UIColor?) { + self.location = location + self.length = length + self.imageName = imageName + self.imageURL = imageURL + self.image = image + self.frame = frame + self.tintColor = tintColor + fetchImage() + } + + //-------------------------------------------------- + // MARK: - Equatable + //-------------------------------------------------- + public static func == (lhs: AtomicImageLabelAttribute, rhs: AtomicImageLabelAttribute) -> Bool { + lhs.isEqual(rhs) + } + + public func isEqual(_ equatable: AtomicImageLabelAttribute) -> Bool { + return id == equatable.id && range == equatable.range && imageName == equatable.imageName && imageURL == equatable.imageURL + } + + //-------------------------------------------------- + // MARK: - Private Functions + //-------------------------------------------------- + private func fetchImage() { + if image != nil { return } + + //get a local asset + if let imageName { + if let coreImage = MVMCoreCache.shared()?.getImageFromRegisteredBundles(imageName) { + image = coreImage + } + + if let vdsImage = BundleManager.shared.image(for: imageName) { + image = vdsImage + } + }//get from url + else if let imageURL { + DispatchQueue.global(qos: .default).async { + MVMCoreCache.shared()?.getImage(imageURL, useWidth: false, widthForS7: 0, useHeight: false, heightForS7: 0, localFallbackImageName: nil) { [weak self] image, data, _ in + DispatchQueue.main.sync { + self?.image = image + } + } + } + } + } + + //-------------------------------------------------- + // MARK: - Public Functions + //-------------------------------------------------- + public func getAttachment() throws -> NSTextAttachment { + if let image { + let attachment = NSTextAttachment() + attachment.image = tintColor != nil ? image.withTintColor(tintColor!) : image + attachment.bounds = frame ?? .init(x: 0, y: 0, width: image.size.width, height: image.size.height) + return attachment + } else { + throw Error.imageNotSet + } + } +} + diff --git a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift new file mode 100644 index 00000000..09fc5dcd --- /dev/null +++ b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift @@ -0,0 +1,31 @@ +// +// VDS-TextStyle.swift +// MVMCoreUI +// +// Created by Matt Bruce on 1/25/23. +// Copyright © 2023 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS + +extension Styler.Font { + //Converts Legacy Font to a VDS.TextStyle + public func vdsTextStyle() -> VDS.TextStyle? { + let updatedRaw = rawValue.replacingOccurrences(of: "Regular", with: "") + let newRaw = updatedRaw.prefix(1).lowercased() + updatedRaw.dropFirst() + guard let style = VDS.TextStyle(rawValue: newRaw) else { return nil } + return style + } + + public func vdsSubsetStyle() -> T? { + guard let style = vdsTextStyle() else { return nil } + guard let rawValue = style.rawValue as? T.RawValue, + let found = T(rawValue: rawValue) else { + print("Style: \(style.rawValue) is not in enum \(T.self)\ronly these cases exist:\r\(T.allCases)") + return nil + } + return found + } +} + diff --git a/MVMCoreUI/Atomic/Extensions/VDS-Tilelet+Codable.swift b/MVMCoreUI/Atomic/Extensions/VDS-Tilelet+Codable.swift new file mode 100644 index 00000000..17bca4b5 --- /dev/null +++ b/MVMCoreUI/Atomic/Extensions/VDS-Tilelet+Codable.swift @@ -0,0 +1,75 @@ +// +// VDS-Tilelet+Codable.swift +// MVMCoreUI +// +// Created by Matt Bruce on 1/25/23. +// Copyright © 2023 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS + +extension Tilelet.BadgeModel: Codable { + private enum CodingKeys: String, CodingKey { + case text, fillColor, surface, numberOfLines, maxWidth + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let text = try container.decode(String.self, forKey: .text) + let fillColor = try container.decodeIfPresent(Badge.FillColor.self, forKey: .fillColor) ?? .red + let surface = try container.decodeIfPresent(Surface.self, forKey: .surface) ?? .light + let numberOfLines = try container.decodeIfPresent(Int.self, forKey: .numberOfLines) ?? 0 + let maxWidth = try container.decodeIfPresent(CGFloat.self, forKey: .maxWidth) + self.init(text: text, fillColor: fillColor, surface: surface, numberOfLines: numberOfLines, maxWidth: maxWidth) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(fillColor, forKey: .fillColor) + try container.encode(surface, forKey: .surface) + try container.encode(numberOfLines, forKey: .numberOfLines) + try container.encodeIfPresent(maxWidth, forKey: .maxWidth) + } +} + +extension Tilelet.DescriptiveIcon: Codable { + private enum CodingKeys: String, CodingKey { + case name, size, surface + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let name = try container.decode(Icon.Name.self, forKey: .name) + let size = try container.decodeIfPresent(Icon.Size.self, forKey: .size) ?? .medium + let surface = try container.decodeIfPresent(Surface.self, forKey: .surface) ?? .dark + self.init(name: name, size: size, surface: surface) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(name, forKey: .name) + try container.encode(size, forKey: .size) + try container.encode(surface, forKey: .surface) + } +} + +extension Tilelet.DirectionalIcon: Codable { + private enum CodingKeys: String, CodingKey { + case size, surface + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let size = try container.decodeIfPresent(Icon.Size.self, forKey: .size) ?? .medium + let surface = try container.decodeIfPresent(Surface.self, forKey: .surface) ?? .dark + self.init(size: size, surface: surface) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(size, forKey: .size) + try container.encode(surface, forKey: .surface) + } +} + diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyTextModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyTextModel.swift index 16d75035..3ed92e02 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyTextModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableCheckboxBodyTextModel.swift @@ -7,7 +7,7 @@ // -open class ListLeftVariableCheckboxBodyTextModel: ListItemModel, MoleculeModelProtocol { +open class ListLeftVariableCheckboxBodyTextModel: ListItemModel, MoleculeModelProtocol, ParentMoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -16,6 +16,10 @@ open class ListLeftVariableCheckboxBodyTextModel: ListItemModel, MoleculeModelPr public var checkbox: CheckboxModel public var headlineBody: HeadlineBodyModel + public var children: [MoleculeModelProtocol] { + [checkbox, headlineBody] + } + //-------------------------------------------------- // MARK: - Initializer //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift index 288b0591..94be5a53 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/OneColumn/ListOneColumnFullWidthTextBodyTextModel.swift @@ -9,7 +9,7 @@ import Foundation -public class ListOneColumnFullWidthTextBodyTextModel: ListItemModel, MoleculeModelProtocol { +public class ListOneColumnFullWidthTextBodyTextModel: ListItemModel, MoleculeModelProtocol, ParentMoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -35,6 +35,10 @@ public class ListOneColumnFullWidthTextBodyTextModel: ListItemModel, MoleculeMod headlineBody.style = .item } + public var children: [MoleculeModelProtocol] { + return [headlineBody] + } + //-------------------------------------------------- // MARK: - Keys //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift index db093a9a..21a4b08c 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift @@ -7,7 +7,7 @@ // -public class ListRightVariableButtonAllTextAndLinksModel: ListItemModel, MoleculeModelProtocol { +public class ListRightVariableButtonAllTextAndLinksModel: ListItemModel, MoleculeModelProtocol, ParentMoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -36,6 +36,10 @@ public class ListRightVariableButtonAllTextAndLinksModel: ListItemModel, Molecul self.button.style = .secondary } + public var children: [MoleculeModelProtocol] { + return [button, eyebrowHeadlineBodyLink] + } + //-------------------------------------------------- // MARK: - Keys //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift index a3452e7b..20df223a 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift @@ -47,6 +47,11 @@ self.body = body } + public init(headline: LabelModel, body: LabelModel) { + self.headline = headline + self.body = body + } + //----------------------------------------------------- // MARK: - Codec //----------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Protocols/VDSMoleculeViewProtocol.swift b/MVMCoreUI/Atomic/Protocols/VDSMoleculeViewProtocol.swift new file mode 100644 index 00000000..41349f2c --- /dev/null +++ b/MVMCoreUI/Atomic/Protocols/VDSMoleculeViewProtocol.swift @@ -0,0 +1,32 @@ +// +// VDSMoleculeViewProtocol.swift +// MVMCoreUI +// +// Created by Matt Bruce on 10/19/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore + +///----------------------------------------------------------------------------- +///MARK: -- VDSMoleculeViewProtocol (Contract between VDS -> Atomic) +///----------------------------------------------------------------------------- +public protocol VDSMoleculeViewProtocol: MoleculeViewProtocol, MVMCoreViewProtocol { + associatedtype ViewModel: MoleculeModelProtocol + var viewModel: ViewModel! { get set } + var delegateObject: MVMCoreUIDelegateObject? { get set } + var additionalData: [AnyHashable: Any]? { get set } + func viewModelDidUpdate() +} + +extension VDSMoleculeViewProtocol { + public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let castedModel = model as? ViewModel else { return } + self.delegateObject = delegateObject + self.additionalData = additionalData + viewModel = castedModel + viewModelDidUpdate() + } +} + diff --git a/MVMCoreUI/Atomic/Templates/ThreeLayerPageTemplateModel.swift b/MVMCoreUI/Atomic/Templates/ThreeLayerPageTemplateModel.swift index e3ada79d..ab4f29d5 100644 --- a/MVMCoreUI/Atomic/Templates/ThreeLayerPageTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/ThreeLayerPageTemplateModel.swift @@ -7,12 +7,12 @@ // -@objcMembers public class ThreeLayerPageTemplateModel: ThreeLayerModelBase { +@objcMembers open class ThreeLayerPageTemplateModel: ThreeLayerModelBase { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - public override class var identifier: String { "threeLayer" } + open override class var identifier: String { "threeLayer" } public var middle: MoleculeModelProtocol? public override var rootMolecules: [MoleculeModelProtocol] { @@ -51,7 +51,7 @@ try super.init(from: decoder) } - public override func encode(to encoder: Encoder) throws { + open override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeModelIfPresent(header, forKey: .middle) diff --git a/MVMCoreUI/BaseClasses/Control.swift b/MVMCoreUI/BaseClasses/Control.swift index 865e3a8e..91cee30f 100644 --- a/MVMCoreUI/BaseClasses/Control.swift +++ b/MVMCoreUI/BaseClasses/Control.swift @@ -9,7 +9,7 @@ import UIKit -@objcMembers open class Control: UIControl, MoleculeViewProtocol { +open class Control: UIControl, MoleculeViewProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index c6c64f3a..f148c1cb 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -132,9 +132,9 @@ import MVMCore try parsePageJSON() } catch let parsingError { // Log all parsing errors and fail load. - handleLoggingFor(parsingError: parsingError) - if let errorObject = MVMCoreErrorObject.createErrorObject(for: parsingError, location: MVMCoreLoadHandler.sharedGlobal()?.errorLocation(forRequest: loadObject)) { + if let errorObject = MVMCoreLoadHandler.sharedGlobal()?.error(for: loadObject, causedBy: parsingError) { errorObject.messageToDisplay = MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess) + errorObject.messageToLog = describe(parsingError: parsingError) error.pointee = errorObject } return false @@ -146,40 +146,54 @@ import MVMCore } } + ///Check with behavior if it can continue + do{ + let allFinishedProcessingLoad = try executeThrowingBehaviors {(behavior: PageMoleculeTransformationBehavior) in + return try behavior.shouldFinishProcessingLoad(loadObject) + } + guard allFinishedProcessingLoad else { return false } + } catch let behaviorError { + if let errorObject = MVMCoreErrorObject.createErrorObject(for: behaviorError, location: MVMCoreLoadHandler.sharedGlobal()?.errorLocation(forRequest: loadObject)) { + error.pointee = errorObject + } + return false + } + return true } - func handleLoggingFor(parsingError: Error) { + func describe(parsingError: Error) -> String { if let registryError = parsingError as? ModelRegistry.Error { switch (registryError) { case .decoderErrorModelNotMapped(let identifier, let codingKey, let codingPath) where identifier != nil && codingKey != nil && codingPath != nil: - MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Model identifier \"\(identifier!)\" is not mapped for \"\(codingKey!.stringValue)\" @ \(codingPath!.map { return $0.stringValue })") + return "Error parsing template. Model identifier \"\(identifier!)\" is not mapped for \"\(codingKey!.stringValue)\" @ \(codingPath!.map { return $0.stringValue })" - case .decoderErrorObjectNotPresent(let codingKey, codingPath: let codingPath): - MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Required model \"\(codingKey.stringValue)\" was not found @ \(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: - MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Registry error: \(registryError)") + 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): - MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Key \(codingKey.stringValue) was not found @ \(context.codingPath.map { return $0.stringValue })") + return "Error parsing template. Required key \(codingKey.stringValue) was not found @ \(context.codingPath.map { return $0.stringValue })" case .valueNotFound(_, let context): - MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Value not found @ \(context.codingPath.map { return $0.stringValue })") + return "Error parsing template. Value not found @ \(context.codingPath.map { return $0.stringValue })" case .typeMismatch(_, let context): - MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Type mismatch @ \(context.codingPath.map { return $0.stringValue })") + return "Error parsing template. Value type mismatch @ \(context.codingPath.map { return $0.stringValue })" case .dataCorrupted(let context): - MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Data corrupted @ \(context.codingPath.map { return $0.stringValue })") + return "Error parsing template. Data corrupted @ \(context.codingPath.map { return $0.stringValue })" @unknown default: - MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: \(parsingError)") + return "Error parsing template. \((parsingError as NSError).localizedFailureReason ?? parsingError.localizedDescription)" } } + return "Error parsing template. \((parsingError as NSError).localizedFailureReason ?? parsingError.localizedDescription)" } open func parsePageJSON() throws { } @@ -200,7 +214,8 @@ import MVMCore } guard modulesRequired.count == 0 else { - if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorCritical), messageToLog: modulesRequired.description, code: ErrorCode.requiredModuleNotPresent.rawValue, domain: ErrorDomainNative, location: MVMCoreLoadHandler.sharedGlobal()?.errorLocation(forRequest: loadObject!)) { + if let loadObject = loadObject, let errorObject = MVMCoreLoadHandler.sharedGlobal()?.error(for: loadObject, withTitle:nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorCritical), code: ErrorCode.requiredModuleNotPresent.rawValue, domain: ErrorDomainNative) { + errorObject.messageToLog = modulesRequired.description error.pointee = errorObject } return false @@ -473,6 +488,14 @@ import MVMCore open func handleAction(error: Error, model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) { let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: MVMCoreActionHandler.getErrorLocation(with: delegateObject?.actionDelegate, actionType: model.actionType))! + + switch (model) { + case let model as ActionOpenPageModel: + errorObject.silentError = model.background ?? false + default: + errorObject.silentError = false + } + MVMCoreUIActionHandler.shared()?.defaultHandleActionError(errorObject, additionalData: additionalData) } diff --git a/MVMCoreUI/Behaviors/Protocols/PageBehaviorHandlerProtocol.swift b/MVMCoreUI/Behaviors/Protocols/PageBehaviorHandlerProtocol.swift index 971a052b..9e5c6a6d 100644 --- a/MVMCoreUI/Behaviors/Protocols/PageBehaviorHandlerProtocol.swift +++ b/MVMCoreUI/Behaviors/Protocols/PageBehaviorHandlerProtocol.swift @@ -44,4 +44,8 @@ public extension PageBehaviorHandlerProtocol { func executeBehaviors(_ behaviorBlock: (_ behavior: T) -> Void) { behaviors?.compactMap { $0 as? T }.forEach { behaviorBlock($0) } } + + func executeThrowingBehaviors(_ behaviourBlock: (_ behavior: T) throws -> Bool) throws -> Bool { + return try behaviors?.compactMap({$0 as? T}).allSatisfy({ return try behaviourBlock($0) }) ?? true + } } diff --git a/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift b/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift index d5f65323..543ee60c 100644 --- a/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift +++ b/MVMCoreUI/Behaviors/Protocols/PageBehaviorProtocol.swift @@ -32,6 +32,7 @@ public protocol PageMoleculeTransformationBehavior: PageBehaviorProtocol { func didSetupMolecule(view: MoleculeViewProtocol, withModel: MoleculeModelProtocol) func willSetupNavigationBar(with model: NavigationItemModelProtocol, updating view: UINavigationBar) func didSetupNavigationBar(view: UINavigationBar, with model: NavigationItemModelProtocol) + func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject) throws -> Bool } public extension PageMoleculeTransformationBehavior { @@ -41,6 +42,7 @@ public extension PageMoleculeTransformationBehavior { func didSetupMolecule(view: MoleculeViewProtocol, withModel: MoleculeModelProtocol) {} func willSetupNavigationBar(with model: NavigationItemModelProtocol, updating view: UINavigationBar) {} func didSetupNavigationBar(view: UINavigationBar, with model: NavigationItemModelProtocol) {} + func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject) throws -> Bool { return true } } public protocol PageVisibilityBehavior: PageBehaviorProtocol { diff --git a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift index 39d100d4..5632c697 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift @@ -71,7 +71,8 @@ open class CoreUIModelMapping: ModelMapping { ModelRegistry.register(handler: WebView.self, for: WebViewModel.self) ModelRegistry.register(handler: LoadingSpinner.self, for: LoadingSpinnerModel.self) ModelRegistry.register(handler: Video.self, for: VideoModel.self) - + ModelRegistry.register(handler: Tilelet.self, for: TileletModel.self) + // MARK:- Horizontal Combination Molecules ModelRegistry.register(handler: StringAndMoleculeView.self, for: StringAndMoleculeModel.self) ModelRegistry.register(handler: ImageHeadlineBody.self, for: ImageHeadlineBodyModel.self) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift index 24d57f8d..3de8fdde 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIActionHandler.swift @@ -41,6 +41,7 @@ import SafariServices open override func defaultHandleActionError(_ error: MVMCoreErrorObject, additionalData: [AnyHashable : Any]?) { super.defaultHandleActionError(error, additionalData: additionalData) guard !error.silentError else { return } + error.silentError = true // Silence if this error is triggered again. (Legacy action handler flow.) Task(priority: .userInitiated) { @MainActor in let alertObject = MVMCoreAlertObject.init(popupAlertWithError: error, isGreedy: false)! MVMCoreAlertHandler.shared()?.showAlert(with: alertObject) diff --git a/Scripts/build_aggregate.sh b/Scripts/build_aggregate.sh index 84c7f4fb..911a4002 100755 --- a/Scripts/build_aggregate.sh +++ b/Scripts/build_aggregate.sh @@ -15,6 +15,10 @@ UNIVERSAL_OUTPUTFOLDER="${BUILD_DIR}/universal" # Update to use .xcframework sed -i '' 's|MVMCore.framework \*\/ = {isa.*};|MVMCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMCore.xcframework; path = ../SharedFrameworks/MVMCore.xcframework; sourceTree = ""; };|g' ./MVMCoreUI.xcodeproj/project.pbxproj sed -i '' 's/MVMCore.framework/MVMCore.xcframework/g' ./MVMCoreUI.xcodeproj/project.pbxproj + +sed -i '' 's|VDS.framework \*\/ = {isa.*};|VDS.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDS.xcframework; path = ../SharedFrameworks/VDS.xcframework; sourceTree = ""; };|g' ./MVMCoreUI.xcodeproj/project.pbxproj +sed -i '' 's/VDS.framework/VDS.xcframework/g' ./MVMCoreUI.xcodeproj/project.pbxproj + sed -i '' "s|path = \.\.\/SharedFrameworks|path = ${FRAMEWORKS_DIR}|g" ./MVMCoreUI.xcodeproj/project.pbxproj # Build device archive diff --git a/Scripts/download_dependencies.sh b/Scripts/download_dependencies.sh index cc6bc209..ec05cfd8 100755 --- a/Scripts/download_dependencies.sh +++ b/Scripts/download_dependencies.sh @@ -8,7 +8,7 @@ # Create new aggregate builds if [ -z $ARTIFACTORY_URL ]; then - ARTIFACTORY_URL="https://oneartifactoryprod.verizon.com/artifactory" + ARTIFACTORY_URL="https://oneartifactoryci.verizon.com/artifactory" fi BUILD_DIR=$(xcodebuild -showBuildSettings -project ./MVMCoreUI.xcodeproj | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-) @@ -18,8 +18,10 @@ if [ ! -d $FRAMEWORKS_DIR ]; then mkdir -p $FRAMEWORKS_DIR fi -./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/MVMCore.xcframework" BPHV_MobileFirst_IOS/com/vzw/hss/myverizon/MVMCore/3.0/MVMCore-3.0-Debug-SNAPSHOT.zip +./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/MVMCore.xcframework" BPHV_MobileFirst_IOS/com/vzw/hss/myverizon/MVMCore/3.1/MVMCore-3.1-Debug-SNAPSHOT.zip -./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/VDSColorTokens.xcframework" GVJV_VDS_Maven/%40vds-tokens/ios/VDSColorTokens.1.0.6.xcframework.zip +./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/VDS.xcframework" BPHV_MobileFirst_IOS/com/vzw/hss/myverizon/VDS/1.0/VDS-1.0-Debug-SNAPSHOT.zip + +./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/VDSColorTokens.xcframework" GVJV_VDS_Maven/@vds-tokens/ios/VDSColorTokens.1.0.6.xcframework.zip ./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/VDSFormControlsTokens.xcframework" GVJV_VDS_Maven/@vds-tokens/ios/VDSFormControlsTokens.1.0.7.xcframework.zip diff --git a/Scripts/upload_core_ui_frameworks.sh b/Scripts/upload_core_ui_frameworks.sh index a8cf475b..b6092993 100755 --- a/Scripts/upload_core_ui_frameworks.sh +++ b/Scripts/upload_core_ui_frameworks.sh @@ -17,7 +17,7 @@ fi # Create new aggregate builds if [ -z $ARTIFACTORY_URL ]; then - ARTIFACTORY_URL="https://oneartifactoryprod.verizon.com/artifactory" + ARTIFACTORY_URL="https://oneartifactoryci.verizon.com/artifactory" fi # Upload