From db424ce04a45d2d6bcf499d360536e8270cea920 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 22 Dec 2020 16:35:21 -0500 Subject: [PATCH] field errors start --- .../Templates/MoleculeListTemplate.swift | 46 +++++++--- .../BaseControllers/ViewController.swift | 92 +++++++++---------- .../FormUIHelpers/FormFieldProtocol.swift | 5 +- 3 files changed, 79 insertions(+), 64 deletions(-) diff --git a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift index 4b6b565e..e23d2ccd 100644 --- a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift @@ -19,7 +19,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol var observer: NSKeyValueObservation? public var templateModel: ListPageTemplateModel? - + //-------------------------------------------------- // MARK: - Computed Properties //-------------------------------------------------- @@ -41,7 +41,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- - + open override func parsePageJSON() throws { try parseTemplate(json: loadObject?.pageJSON) try super.parsePageJSON() @@ -54,8 +54,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol open override func viewForTop() -> UIView { guard let headerModel = templateModel?.header, - let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) - else { return super.viewForTop() } + let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) + else { return super.viewForTop() } // Temporary, Default the horizontal padding if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil { @@ -67,8 +67,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol override open func viewForBottom() -> UIView { guard let footerModel = templateModel?.footer, - let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) - else { return super.viewForBottom() } + let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) + else { return super.viewForBottom() } return molecule } @@ -86,7 +86,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol //Handle scroll handleScrollToSpecificRow() } - + //-------------------------------------------------- // MARK: - Handle scroll to spefic row @@ -117,12 +117,12 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { guard let moleculeInfo = getMoleculeInfo(for: indexPath), - let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) - else { return 0 } + let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) + else { return 0 } return estimatedHeight } - + open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return moleculesInfo?.count ?? 0 } @@ -130,8 +130,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let moleculeInfo = getMoleculeInfo(for: indexPath), - let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) - else { return UITableViewCell() } + let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) + else { return UITableViewCell() } (cell as? MoleculeViewProtocol)?.reset() (cell as? MoleculeListCellProtocol)?.setLines(with: templateModel?.line, delegateObject: delegateObjectIVar, additionalData: nil, indexPath: indexPath) @@ -156,6 +156,24 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol // MARK: - MoleculeDelegateProtocol //-------------------------------------------------- + public override func handleFieldErrors(_ fieldErrors: [Any]?, loadObject: MVMCoreLoadObject) { + super.handleFieldErrors(fieldErrors, loadObject: loadObject) + + for case let fieldError as [AnyHashable: Any] in fieldErrors ?? [] { + + guard let fieldKey = fieldError["fieldKey"] as? String, + let userError = fieldError["userMessage"] as? String, + let entryFieldModel = formValidator?.fields[fieldKey] as? EntryFieldModel + else { continue } + + entryFieldModel.errorMessage = userError + + DispatchQueue.main.async { + let c = self.tableView.cellForRow(at: IndexPath(row: 0, section: 0))?.layoutSubviews() + } + } + } + open override func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { if let tableView = tableView { @@ -222,8 +240,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol func createMoleculeInfo(with listItem: MoleculeModelProtocol?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { guard let listItem = listItem, - let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem) - else { return nil } + let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem) + else { return nil } let moleculeName = moleculeClass.nameForReuse(with: listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 33c47344..5fc57de7 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -18,12 +18,8 @@ import UIKit @objc public var loadObject: MVMCoreLoadObject? public var model: MVMControllerModelProtocol? public var pageModel: PageModelProtocol? { - get { - return model - } - set { - model = newValue as? MVMControllerModelProtocol - } + get { model } + set { model = newValue as? MVMControllerModelProtocol } } /// Set if this page is containted in a manager. @@ -31,12 +27,10 @@ import UIKit /// A temporary iVar backer for delegateObject() until we change the protocol public lazy var delegateObjectIVar: MVMCoreUIDelegateObject = { - return MVMCoreUIDelegateObject.create(withDelegateForAll: self) + MVMCoreUIDelegateObject.create(withDelegateForAll: self) }() - public func delegateObject() -> DelegateObject? { - return delegateObjectIVar - } + public func delegateObject() -> DelegateObject? { delegateObjectIVar } public var formValidator: FormValidator? @@ -52,7 +46,7 @@ import UIKit /// Checks if the screen width has changed open func screenSizeChanged() -> Bool { - return !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1) + !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1) } //-------------------------------------------------- @@ -61,8 +55,8 @@ import UIKit open func observeForResponseJSONUpdates() { guard !observingForResponses, - (pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0) - else { return } + (pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0) + else { return } observingForResponses = true NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil) @@ -80,28 +74,28 @@ import UIKit } open func modulesToListenFor() -> [String]? { - return loadObject?.requestParameters?.modules as? [String] + loadObject?.requestParameters?.modules as? [String] } @objc open func responseJSONUpdated(notification: Notification) { // Checks for a page we are listening for. var newData = false if let pagesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyPageMap), - let pageType = pagesToListenFor()?.first(where: { (pageTypeListened) -> Bool in - guard let page = pagesLoaded.optionalDictionaryForKey(pageTypeListened), - let pageType = page.optionalStringForKey(KeyPageType), - pageType == pageTypeListened - else { return false } - - return true - }) { + let pageType = pagesToListenFor()?.first(where: { (pageTypeListened) -> Bool in + guard let page = pagesLoaded.optionalDictionaryForKey(pageTypeListened), + let pageType = page.optionalStringForKey(KeyPageType), + pageType == pageTypeListened + else { return false } + + return true + }) { newData = true loadObject?.pageJSON = pagesLoaded.optionalDictionaryForKey(pageType) } // Checks for modules we are listening for. if let modulesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyModuleMap), - let modulesListened = modulesToListenFor() { + let modulesListened = modulesToListenFor() { for moduleName in modulesListened { if let module = modulesLoaded.optionalDictionaryForKey(moduleName) { newData = true @@ -196,9 +190,9 @@ import UIKit open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: AutoreleasingUnsafeMutablePointer) -> Bool { guard let pageType = loadObject?.pageType, - var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType), - !modulesRequired.isEmpty - else { return true } + var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType), + !modulesRequired.isEmpty + else { return true } guard let loadedModules = loadObject?.modulesJSON else { return false } @@ -263,8 +257,8 @@ import UIKit /// Sets the navigation item for this view controller. open func setNavigationItem() { guard let navigationItemModel = getNavigationModel(), - let navigationController = navigationController - else { return } + let navigationController = navigationController + else { return } // Utilize helper function to set the navigation item state. NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self) @@ -273,9 +267,9 @@ import UIKit /// Sets the appearance of the navigation bar based on the model. open func setNavigationBar() { guard let navigationItemModel = getNavigationModel(), - let navigationController = navigationController else { - MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() - return + let navigationController = navigationController else { + MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() + return } // Utilize helper function to set the split view and navigation item state. @@ -405,7 +399,7 @@ import UIKit } open override var supportedInterfaceOrientations: UIInterfaceOrientationMask { - return MVMCoreGetterUtility.isOnIPad() ? UIInterfaceOrientationMask.all : UIInterfaceOrientationMask.portrait + MVMCoreGetterUtility.isOnIPad() ? UIInterfaceOrientationMask.all : UIInterfaceOrientationMask.portrait } open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -424,6 +418,7 @@ import UIKit open func viewControllerReady(inManager manager: UIViewController & MVMCoreViewManagerProtocol) { pageShown() } + //-------------------------------------------------- // MARK: - MVMCoreLoadDelegateProtocol //-------------------------------------------------- @@ -435,15 +430,18 @@ import UIKit // Open the support panel if error == nil, - loadObject?.requestParameters?.openSupportPanel ?? (loadObject?.systemParametersJSON?.boolForKey(KeyOpenSupport) ?? false) == true { + loadObject?.requestParameters?.openSupportPanel ?? (loadObject?.systemParametersJSON?.boolForKey(KeyOpenSupport) ?? false) == true { MVMCoreUISession.sharedGlobal()?.splitViewController?.showRightPanel(animated: true) } } - + /// Override this method to avoid adding form params. open func addFormParams(_ requestParameters: MVMCoreRequestParameters) { formValidator?.addFormParams(requestParameters: requestParameters) } + + public func handleFieldErrors(_ fieldErrors: [Any]?, loadObject: MVMCoreLoadObject) { } + //-------------------------------------------------- // MARK: - MVMCoreActionDelegateProtocol //-------------------------------------------------- @@ -469,9 +467,9 @@ import UIKit open func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? { guard let moduleJSON = loadObject?.modulesJSON?.optionalDictionaryForKey(moleculeName), - let moleculeName = moduleJSON.optionalStringForKey("moleculeName"), - let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) - else { return nil } + let moleculeName = moduleJSON.optionalStringForKey("moleculeName"), + let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) + else { return nil } do { return try modelType.decode(jsonDict: moduleJSON) as? MoleculeModelProtocol @@ -483,8 +481,8 @@ import UIKit } // Needed otherwise when subclassed, the extension gets called. - open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {} - open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { return nil } + open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { } + open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { nil } open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) { } open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) { } @@ -514,13 +512,13 @@ import UIKit } open func showRightPanelForScreenBeforeLaunchApp() -> Bool { - return loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false + loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false } // TODO: make molecular open func isOverridingRightButton() -> Bool { guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink") - else { return false } + else { return false } MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject()) return true } @@ -528,7 +526,7 @@ import UIKit // TODO: make molecular open func isOverridingLeftButton() -> Bool { guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink") - else { return false } + else { return false } MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject()) return true } @@ -536,8 +534,8 @@ import UIKit // Eventually will be moved to Model open func bottomProgress() -> Float? { guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), - let progress = Float(progressString) - else { return nil } + let progress = Float(progressString) + else { return nil } return progress / Float(100) } @@ -558,8 +556,8 @@ import UIKit // TODO: Make this into a protocol if UIAccessibility.isVoiceOverRunning { if let toolBar = textField.inputAccessoryView as? UIToolbar, - let _ = toolBar.items?.last, - let pickerView = textField.inputView as? UIPickerView { + let _ = toolBar.items?.last, + let pickerView = textField.inputView as? UIPickerView { view.accessibilityElements = [pickerView, toolBar] } @@ -610,6 +608,6 @@ import UIKit //-------------------------------------------------- func executeBehaviors(_ behaviorBlock:(_ behavior:T)->Void) { - model?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) } + model?.behaviors?.compactMap { $0 as? T }.forEach { behaviorBlock($0) } } } diff --git a/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift b/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift index b9d2defe..f769e040 100644 --- a/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift @@ -22,7 +22,6 @@ public protocol FormFieldProtocol: FormItemProtocol { } extension FormFieldProtocol { - var baseValue: AnyHashable? { - return nil - } + + var baseValue: AnyHashable? { nil } }