diff --git a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift index 02373958..dfffcded 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Toggle.swift @@ -392,8 +392,23 @@ public typealias ActionBlockConfirmation = () -> (Bool) accessibilityLabel = accessibileString } - if let actionMap = model.action?.toJSON() { - didToggleAction = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } + let actionMap = model.action?.toJSON() + let alternateActionMap = model.alternateAction?.toJSON() + if actionMap != nil || alternateActionMap != nil { + didToggleAction = { [weak self] in + guard let self = self else { return } + if self.isOn { + if actionMap != nil { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } else { + if alternateActionMap != nil { + MVMCoreActionHandler.shared()?.handleAction(with: alternateActionMap, additionalData: additionalData, delegateObject: delegateObject) + } else if actionMap != nil { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } + } } } diff --git a/MVMCoreUI/Atomic/Protocols/TabBarProtocol.swift b/MVMCoreUI/Atomic/Protocols/TabBarProtocol.swift index 1bf79795..19c7e107 100644 --- a/MVMCoreUI/Atomic/Protocols/TabBarProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/TabBarProtocol.swift @@ -9,6 +9,9 @@ import Foundation @objc public protocol TabBarProtocol { + + var delegateObject: MVMCoreUIDelegateObject? { get set } + /// Should visually select the given tab index. @objc func highlightTab(at index: Int) diff --git a/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift b/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift index 04280042..4696ce38 100644 --- a/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift +++ b/MVMCoreUI/Atomic/Protocols/TemplateProtocol.swift @@ -11,16 +11,23 @@ import Foundation public protocol TemplateProtocol: AnyObject { associatedtype TemplateModel: TemplateModelProtocol var templateModel: TemplateModel? { get set } + + func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> TemplateModel } public extension TemplateProtocol where Self: ViewController { + func parseTemplate(json: [AnyHashable: Any]?) throws { guard let pageJSON = json else { return } let data = try JSONSerialization.data(withJSONObject: pageJSON) let decoder = JSONDecoder() try decoder.add(delegateObject: delegateObjectIVar) - let templateModel = try decoder.decode(TemplateModel.self, from: data) - self.templateModel = templateModel + self.templateModel = try decodeTemplate(using: decoder, from: data) self.pageModel = templateModel as? MVMControllerModelProtocol } + + func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> TemplateModel { + return try decoder.decode(TemplateModel.self, from: data) + } + } diff --git a/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift b/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift index b239bd81..b2b52573 100644 --- a/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/ListPageTemplateModel.swift @@ -8,12 +8,12 @@ import Foundation -@objcMembers public class ListPageTemplateModel: ThreeLayerModelBase { +@objcMembers open class ListPageTemplateModel: ThreeLayerModelBase { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - public override class var identifier: String { + open override class var identifier: String { return "list" } public var molecules: [ListItemModelProtocol & MoleculeModelProtocol]? @@ -49,7 +49,7 @@ import Foundation 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.encodeModelsIfPresent(molecules, forKey: .molecules) diff --git a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift index 4be4e025..372ae778 100644 --- a/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/MoleculeListTemplate.swift @@ -28,6 +28,11 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol try super.parsePageJSON() } + // For subclassing the model. + open func decodeTemplate(using decoder: JSONDecoder, from data: Data) throws -> ListPageTemplateModel { + return try decoder.decode(ListPageTemplateModel.self, from: data) + } + open override var loadObject: MVMCoreLoadObject? { didSet { guard loadObject != oldValue else { return } diff --git a/MVMCoreUI/Atomic/Templates/TemplateModel.swift b/MVMCoreUI/Atomic/Templates/TemplateModel.swift index 01753414..bb8bb104 100644 --- a/MVMCoreUI/Atomic/Templates/TemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/TemplateModel.swift @@ -9,7 +9,8 @@ import Foundation -@objcMembers public class TemplateModel: MVMControllerModelProtocol, TabPageModelProtocol { +@objcMembers open class TemplateModel: MVMControllerModelProtocol, TabPageModelProtocol { + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Templates/ThreeLayerModelBase.swift b/MVMCoreUI/Atomic/Templates/ThreeLayerModelBase.swift index f0276979..1120b0e7 100644 --- a/MVMCoreUI/Atomic/Templates/ThreeLayerModelBase.swift +++ b/MVMCoreUI/Atomic/Templates/ThreeLayerModelBase.swift @@ -8,7 +8,7 @@ import Foundation -@objcMembers public class ThreeLayerModelBase: TemplateModel, ThreeLayerTemplateModelProtocol { +@objcMembers open class ThreeLayerModelBase: TemplateModel, ThreeLayerTemplateModelProtocol { public var anchorHeader: Bool = false public var header: MoleculeModelProtocol? public var anchorFooter: Bool = false diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 8c63ed51..228dbdcb 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -94,9 +94,10 @@ import UIKit try parsePageJSON() MVMCoreDispatchUtility.performBlock(onMainThread: { self.handleNewDataAndUpdateUI() - // If the screen is showing, can update the navigation controller. - if MVMCoreUIUtility.getCurrentVisibleController() == self.manager ?? self { - self.setNavigationController() + + // Update navigation bar if showing. + if MVMCoreUIUtility.getCurrentVisibleController() == self { + self.setNavigationBar() } }) } catch { @@ -157,7 +158,7 @@ import UIKit open func parsePageJSON() throws { } - + 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 } @@ -196,12 +197,6 @@ import UIKit /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, open func handleNewData() { - // TODO: remove legacy. Temporary, convert legacy to navigation model. - if pageModel?.navigationBar == nil { - let navigationItem = createDefaultLegacyNavigationModel() - pageModel?.navigationBar = navigationItem - } - if formValidator == nil { let rules = pageModel?.formRules formValidator = FormValidator(rules) @@ -210,20 +205,43 @@ import UIKit if let backgroundColor = pageModel?.backgroundColor { view.backgroundColor = backgroundColor.uiColor } + + // Sets up the navigation item based on the data. + setNavigationItem() } // MARK: - Navigation Item (Move to model base) - open func setNavigationController() { + + open func getNavigationModel() -> NavigationItemModelProtocol? { + // TODO: remove legacy. Temporary, convert legacy to navigation model. + if pageModel?.navigationBar == nil { + let navigationItem = createDefaultLegacyNavigationModel() + pageModel?.navigationBar = navigationItem + } + return pageModel?.navigationBar + } + + /// Sets the navigation item for this view controller. + open func setNavigationItem() { + guard let navigationItemModel = getNavigationModel(), + let navigationController = navigationController else { return } + + // We additionally want our left items + navigationItem.leftItemsSupplementBackButton = true + + // Utilize helper function to set the navigation item state. + NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self) + } + + /// Sets the appearance of the navigation bar based on the model. + open func setNavigationBar() { let viewController = manager ?? self - guard let navigationItemModel = pageModel?.navigationBar, + guard let navigationItemModel = getNavigationModel(), let navigationController = viewController.navigationController else { MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() return } - // We additionally want our left items - navigationItem.leftItemsSupplementBackButton = true - // Utilize helper function to set the split view and navigation item state. MVMCoreUISplitViewController.setSplitViewController(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel, leftPanelAccessible: isMasterInitiallyAccessible(), rightPanelAccessible: isSupportInitiallyAccessible(), progress: bottomProgress() ?? 0) } @@ -277,6 +295,7 @@ import UIKit open func updateTabBar() { guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self, let tabModel = pageModel as? TabPageModelProtocol else { return } + MVMCoreUISplitViewController.main()?.tabBar?.delegateObject = delegateObjectIVar if let index = tabModel.tabBarIndex { MVMCoreUISplitViewController.main()?.tabBar?.highlightTab(at: index) } @@ -336,7 +355,7 @@ import UIKit open func pageShown() { // Update the navigation bar ui when view is appearing. - setNavigationController() + setNavigationBar() // Update tab if needed. updateTabBar() @@ -453,7 +472,7 @@ import UIKit // Reset the navigation state. public func splitViewDidReset() { - setNavigationController() + setNavigationBar() } // MARK: - UITextFieldDelegate (Check if this is still needed) diff --git a/MVMCoreUI/Containers/NavigationController.swift b/MVMCoreUI/Containers/NavigationController.swift index 2803792f..0cad62c5 100644 --- a/MVMCoreUI/Containers/NavigationController.swift +++ b/MVMCoreUI/Containers/NavigationController.swift @@ -43,6 +43,14 @@ import UIKit return navigationController } + /// Convenience function for setting the navigation item. + public static func setNavigationItem(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { + viewController.navigationItem.title = navigationItemModel.title + viewController.navigationItem.accessibilityLabel = navigationItemModel.title + viewController.navigationItem.hidesBackButton = (navigationItemModel.backButton != nil) + setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + } + /// Convenience function for setting the navigation buttons. public static func setNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject @@ -70,11 +78,7 @@ import UIKit } /// Convenience function for setting the navigation bar ui, except for the buttons. - public static func setNavigationUI(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { - viewController.navigationItem.title = navigationItemModel.title - viewController.navigationItem.accessibilityLabel = navigationItemModel.title - viewController.navigationItem.hidesBackButton = (navigationItemModel.backButton != nil) - + public static func setNavigationBarUI(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { navigationController.setNavigationBarHidden(navigationItemModel.hidden, animated: true) navigationController.navigationBar.barTintColor = navigationItemModel.backgroundColor?.uiColor ?? .white @@ -90,17 +94,19 @@ import UIKit } } - /// Convenience function for setting navigation bar with model. - public static func set(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) { - setNavigationUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) - setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) - } - /// Convenience setter for legacy files - public static func set(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { + public static func setNavigationItem(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") } - set(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) + setNavigationItem(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) + } + + /// Convenience setter for legacy files + public static func setNavigationBarUI(navigationController: UINavigationController, navigationJSON: [String: Any], viewController: UIViewController) throws { + guard let barModel = try MoleculeObjectMapping.shared()?.getMoleculeModelForJSON(navigationJSON) as? (MoleculeModelProtocol & NavigationItemModelProtocol) else { + throw ModelRegistry.Error.decoderOther(message: "Model not a bar model") + } + setNavigationBarUI(navigationController: navigationController, navigationItemModel: barModel, viewController: viewController) } } diff --git a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift index e62efa29..963834ff 100644 --- a/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift +++ b/MVMCoreUI/Containers/SplitViewController/MVMCoreUISplitViewController+Extension.swift @@ -15,7 +15,7 @@ public extension MVMCoreUISplitViewController { guard let splitView = MVMCoreUISplitViewController.main(), navigationController == splitView.navigationController, navigationController.topViewController == viewController else { - NavigationController.set(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) return } splitView.set(for: viewController, navigationController: navigationController, navigationItemModel: navigationItemModel, leftPanelAccessible: leftPanelAccessible, rightPanelAccessible: rightPanelAccessible, progress: progress) @@ -27,7 +27,7 @@ public extension MVMCoreUISplitViewController { // Setup the panels. setupPanels() - NavigationController.setNavigationUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) setLeftPanelIsAccessible(leftPanelAccessible ?? leftPanelIsAccessible, for: viewController, updateNavigationButtons: false) setRightPanelIsAccessible(rightPanelAccessible ?? rightPanelIsAccessible, for: viewController, updateNavigationButtons: false) @@ -117,7 +117,7 @@ public extension MVMCoreUISplitViewController { guard let splitView = MVMCoreUISplitViewController.main(), navigationController == splitView.navigationController, navigationController.topViewController == viewController else { - NavigationController.set(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) + NavigationController.setNavigationBarUI(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController) return } let progress = progress?.floatValue diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h index 9fec90f5..14da21ad 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h +++ b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.h @@ -17,8 +17,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)defaultLogPageStateForController:(nonnull id )controller; // Action Logging -- (void)defaultLogActionForController:(nonnull id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; -- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nonnull id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; +- (void)defaultLogActionForController:(nullable id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; +- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nullable id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; @end diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m index 0c694cce..45a13acf 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUILoggingHandler.m @@ -13,10 +13,10 @@ - (void)defaultLogPageStateForController:(nonnull id )controller { } -- (void)defaultLogActionForController:(nonnull id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData { +- (void)defaultLogActionForController:(nullable id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData { } -- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nonnull id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData { +- (nullable NSDictionary *)defaultGetActionTrackDataDictionaryForController:(nullable id )controller actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData { return nil; }