From 54292edd49b4b781229b1ade6131a30cdad52528 Mon Sep 17 00:00:00 2001 From: david-swift Date: Sat, 31 Aug 2024 12:36:09 +0200 Subject: [PATCH] Switch to state management using reference types --- Sources/Model/Data Flow/Model.swift | 12 +-- Sources/Model/Data Flow/State.swift | 24 +++--- Sources/Model/Data Flow/StateContent.swift | 38 +++++---- Sources/Model/Data Flow/StateManager.swift | 94 --------------------- Sources/Model/Data Flow/StateProtocol.swift | 4 +- Sources/Model/User Interface/App/App.swift | 11 +-- Sources/View/StateWrapper.swift | 8 +- 7 files changed, 46 insertions(+), 145 deletions(-) diff --git a/Sources/Model/Data Flow/Model.swift b/Sources/Model/Data Flow/Model.swift index 5bc3533..cea2c78 100644 --- a/Sources/Model/Data Flow/Model.swift +++ b/Sources/Model/Data Flow/Model.swift @@ -61,7 +61,7 @@ public protocol Model { public struct ModelData { /// The state value's identifier. - var id: String + var storage: StateContent.Storage /// Whether to force update the views when this value changes. var force: Bool @@ -78,8 +78,8 @@ extension Model { guard let data = model else { return } - StateManager.setState(id: data.id, value: newValue) - StateManager.updateState(id: data.id) + data.storage.value = newValue + data.storage.update = true StateManager.updateViews(force: data.force) } } @@ -98,8 +98,8 @@ extension Model { } var model = getModel() setModel(&model) - StateManager.setState(id: data.id, value: model) - StateManager.updateState(id: data.id) + data.storage.value = model + data.storage.update = true StateManager.updateViews(force: data.force) } @@ -112,7 +112,7 @@ extension Model { guard let data = model else { return self } - return StateManager.getState(id: data.id) as? Self ?? self + return data.storage.value as? Self ?? self } } diff --git a/Sources/Model/Data Flow/State.swift b/Sources/Model/Data Flow/State.swift index 8e01763..32d5c4d 100644 --- a/Sources/Model/Data Flow/State.swift +++ b/Sources/Model/Data Flow/State.swift @@ -18,7 +18,7 @@ public struct State: StateProtocol { } nonmutating set { rawValue = newValue - StateManager.updateState(id: id) + content.update = true StateManager.updateViews(force: forceUpdates) } } @@ -35,42 +35,42 @@ public struct State: StateProtocol { /// Get and set the value without updating the views. public var rawValue: Value { get { - guard let value = StateManager.getState(id: id) as? Value else { + guard let value = content.value as? Value else { let initialValue = getInitialValue() + let storage = StateContent.Storage(value: initialValue) if var model = initialValue as? Model { - model.model = .init(id: id, force: forceUpdates) + model.model = .init(storage: storage, force: forceUpdates) model.setup() - StateManager.setState(id: id, value: model) - StateManager.addConstantID(id) + content.storage = storage + content.value = model } else { - StateManager.setState(id: id, value: initialValue) + content.storage = storage } return initialValue } return value } nonmutating set { - StateManager.setState(id: id, value: newValue) + content.value = newValue } } - /// The state's identifier for the stored value. - var id: String - /// Whether to force update the views when the value changes. var forceUpdates: Bool /// The closure for initializing the state property's value. var getInitialValue: () -> Value + /// The content. + let content: StateContent = .init() + /// Initialize a property representing a state in the view with an autoclosure. /// - Parameters: /// - wrappedValue: The wrapped value. /// - id: An explicit identifier. /// - forceUpdates: Whether to force update all available views when the property gets modified. - public init(wrappedValue: @autoclosure @escaping () -> Value, id: String? = nil, forceUpdates: Bool = false) { + public init(wrappedValue: @autoclosure @escaping () -> Value, forceUpdates: Bool = false) { getInitialValue = wrappedValue - self.id = id ?? UUID().uuidString self.forceUpdates = forceUpdates } diff --git a/Sources/Model/Data Flow/StateContent.swift b/Sources/Model/Data Flow/StateContent.swift index a258b15..aff3663 100644 --- a/Sources/Model/Data Flow/StateContent.swift +++ b/Sources/Model/Data Flow/StateContent.swift @@ -9,30 +9,34 @@ class StateContent { /// The storage. - var storage: Storage { + var storage: Storage? + + /// The value. + var value: Any? { get { - if let internalStorage { - return internalStorage - } - let value = getInitialValue() - let storage = Storage(value: value) - internalStorage = storage - return storage + storage?.value } set { - internalStorage = newValue + if let storage { + storage.value = newValue as Any + } else { + storage = .init(value: newValue as Any) + } + } + } + + /// Whether to update the views. + var update: Bool { + get { + storage?.update ?? false + } + set { + storage?.update = newValue } } - /// The internal storage. - var internalStorage: Storage? - /// The initial value. - private var getInitialValue: () -> Any /// Initialize the content without already initializing the storage or initializing the value. - /// - Parameter initialValue: The initial value. - init(getInitialValue: @escaping () -> Any) { - self.getInitialValue = getInitialValue - } + init() { } /// A class storing the value. class Storage { diff --git a/Sources/Model/Data Flow/StateManager.swift b/Sources/Model/Data Flow/StateManager.swift index b49e4ae..147acb2 100644 --- a/Sources/Model/Data Flow/StateManager.swift +++ b/Sources/Model/Data Flow/StateManager.swift @@ -12,41 +12,10 @@ public enum StateManager { /// Whether to block updates in general. public static var blockUpdates = false - /// Whether to save state. - public static var saveState = true /// The application identifier. static var appID: String? /// The functions handling view updates. static var updateHandlers: [(Bool) -> Void] = [] - /// The state. - static var state: [State] = [] - - /// Information about a piece of state. - struct State { - - /// The state's identifier. - var id: String - /// Old identifiers of the state which need to be saved. - var oldIDs: [String] = [] - /// The state value. - var value: Any? - /// Whether to update in the next iteration. - var update = false - - /// Whether the state's identifiers contain a certain identifier. - /// - Parameter id: The identifier. - /// - Returns: Whether the id is contained. - func contains(id: String) -> Bool { - id == self.id || oldIDs.contains(id) - } - - /// Change the identifier to a new one. - /// - Parameter newID: The new identifier. - mutating func changeID(new newID: String) { - id = newID - } - - } /// Update all of the views. /// - Parameter force: Whether to force all views to update. @@ -57,9 +26,6 @@ public enum StateManager { for handler in updateHandlers { handler(force) } - for state in state where state.update { - updatedState(id: state.id) - } } } @@ -69,64 +35,4 @@ public enum StateManager { updateHandlers.append(handler) } - /// Set the state value for a certain ID. - /// - Parameters: - /// - id: The identifier. - /// - value: The new value. - static func setState(id: String, value: Any?) { - if saveState { - guard let index = state.firstIndex(where: { $0.contains(id: id) }) else { - state.append(.init(id: id, value: value)) - return - } - state[safe: index]?.value = value - } - } - - /// Get the state value for a certain ID. - /// - Parameter id: The identifier. - /// - Returns: The value. - static func getState(id: String) -> Any? { - state[safe: state.firstIndex { $0.contains(id: id) }]?.value - } - - /// Mark the state of a certain id as updated. - /// - Parameter id: The identifier. - static func updateState(id: String) { - if saveState { - state[safe: state.firstIndex { $0.contains(id: id) }]?.update = true - } - } - - /// Mark the state of a certain id as not updated. - /// - Parameter id: The identifier. - static func updatedState(id: String) { - if saveState { - state[safe: state.firstIndex { $0.contains(id: id) }]?.update = false - } - } - - /// Get whether to update the state of a certain id. - /// - Parameter id: The identifier. - /// - Returns: Whether to update the state. - static func getUpdateState(id: String) -> Bool { - state[safe: state.firstIndex { $0.contains(id: id) }]?.update ?? false - } - - /// Change the identifier for a certain state value. - /// - Parameters: - /// - oldID: The old identifier. - /// - newID: The new identifier. - static func changeID(old oldID: String, new newID: String) { - if saveState { - state[safe: state.firstIndex { $0.contains(id: oldID) }]?.changeID(new: newID) - } - } - - /// Save a state's identifier until the program ends. - /// - Parameter id: The identifier. - static func addConstantID(_ id: String) { - state[safe: state.firstIndex { $0.id == id }]?.oldIDs.append(id) - } - } diff --git a/Sources/Model/Data Flow/StateProtocol.swift b/Sources/Model/Data Flow/StateProtocol.swift index cfa6ded..681c510 100644 --- a/Sources/Model/Data Flow/StateProtocol.swift +++ b/Sources/Model/Data Flow/StateProtocol.swift @@ -10,7 +10,7 @@ import Foundation /// An interface for accessing `State` without specifying the generic type. protocol StateProtocol { - /// The identifier for the state property's value. - var id: String { get set } + /// The state content. + var content: StateContent { get } } diff --git a/Sources/Model/User Interface/App/App.swift b/Sources/Model/User Interface/App/App.swift index 9ec9eaa..680c858 100644 --- a/Sources/Model/User Interface/App/App.swift +++ b/Sources/Model/User Interface/App/App.swift @@ -62,16 +62,7 @@ extension App { appInstance.app = Storage(id: appInstance.id) appInstance.app.storage.app = { appInstance } StateManager.addUpdateHandler { force in - var updateProperties = force - for property in appInstance.getState() { - if let oldID = appInstance.app.storage.stateStorage[property.key]?.id { - StateManager.changeID(old: oldID, new: property.value.id) - appInstance.app.storage.stateStorage[property.key]?.id = property.value.id - } - if StateManager.getUpdateState(id: property.value.id) { - updateProperties = true - } - } + let updateProperties = force || appInstance.getState().contains { $0.value.content.update } var removeIndices: [Int] = [] for (index, element) in appInstance.app.storage.sceneStorage.enumerated() { if element.destroy { diff --git a/Sources/View/StateWrapper.swift b/Sources/View/StateWrapper.swift index 5853dcf..00d8f27 100644 --- a/Sources/View/StateWrapper.swift +++ b/Sources/View/StateWrapper.swift @@ -43,12 +43,12 @@ struct StateWrapper: ConvenienceWidget { ) where Data: ViewRenderData { var updateProperties = updateProperties for property in state { - if let oldID = storage.state[property.key]?.id { - StateManager.changeID(old: oldID, new: property.value.id) - storage.state[property.key]?.id = property.value.id + if let storage = storage.state[property.key]?.content.storage { + property.value.content.storage = storage } - if StateManager.getUpdateState(id: property.value.id) { + if property.value.content.update { updateProperties = true + property.value.content.update = false } } guard let storage = storage.content[.mainContent]?.first else { -- 2.36.6